文章目录设计模式一、使用代理的原因
二、代理模式的介绍代理模式分为两种:静态代理模式和动态代理模式。 静态代理模式实际上就是目标对象已确定,即目标对象已经写死。 动态代理模式运行时才确定目标对象。 这里着重于动态代理的实现 三、动态代理的实现:接口实现3.1、具体使用下面的部分操作先可当巫师的咒语。 JDkproxy.class public class JDKProxy { public JDKProxy() { } @SuppressWarnings("unchecked") public static <T> T getProxy(Class<?> klass) { try { Object object = klass.getDeclaredConstructor().newInstance(); return (T) getProxy(object); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } return null; } @SuppressWarnings("unchecked") public static <T> T getProxy(Object object) { Class<?> klass = object.getClass(); ClassLoader classLoader = klass.getClassLoader(); Class<?>[] interfaces = klass.getInterfaces(); return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("前"); result = method.invoke(object, args); System.out.println("后\n"); return result; } }); } } Interfaze.class public interface Interfaze { void doOneThing(String str); String doOtherThing(int num, String str); } MyImplement.class Interfaze接口的实现类 public class MyImplement implements Interfaze{ public MyImplement() { } @Override public void doOneThing(String str) { System.out.println("doOneThing :" str); } @Override public String doOtherThing(int num, String str) { System.out.println("doOtherThing :" num str); return num str; } } Test.class public class Text { public static void main(String[] args) { Interfaze oneproxy = JDKProxy.getProxy(new MyImplement()); oneproxy.doOneThing("abd"); String str = oneproxy.doOtherThing(34, "肥肠"); System.out.println(str); } } 输出结果 根据输出结果,可以看到Interfaze oneproxy可以去调用MyImplement()实现了它的方法。 不禁有人就开始发问了,为什么不用MyImplement()去他调用方法呢?非要绕这么大的弯! 你看输出结果,在执行方法的前后都执行了“前”和“后”的输出语句,这说明什么,我们在一定程度上修改了这个方法,而且没有破坏他。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("前"); result = method.invoke(object, args); System.out.println("后\n"); return result; } //在执行这个真正的方法前,我们有这个方法method,有他的参数args,那完全可以在执行调用的方法之前进行提前处理,在执行调用方法之后进行善后处理。这就是代理模式的真正精髓所在。 3.2、源码分析
此处有两个重要的源码分析点:
下面,我们将主要分析这两处源码。 3.2.1、(关注1)创建动态代理类&对象:通过调用处理器类对象newProxyInstance()<-- 关注1:调用处理器 类的newProxyInstance() --> @SuppressWarnings("unchecked") public static <T> T getProxy(Object object) { //这里的object就是代理对象 Class<?> klass = object.getClass(); ClassLoader classLoader = klass.getClassLoader(); Class<?>[] interfaces = klass.getInterfaces(); //Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回 return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("前"); result = method.invoke(object, args); System.out.println("后\n"); return result; } }); } // 复写InvocationHandler接口的invoke() // 动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke() @Override public Object invoke(Object proxy, Method method, Object[] args) // 参数说明: // 参数1:动态代理对象(即哪个动态代理对象调用了method() // 参数2:目标对象被调用的方法 // 参数3:指定被调用方法的参数 throws Throwable {Object result = null; System.out.println("前"); result = method.invoke(object, args); System.out.println("后\n"); return result; } <-- 关注2:newProxyInstance()源码解析--> // 作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类及其对象实例,并最终返回 public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { // 参数说明: // 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 // 参数2:指定目标对象的实现接口 // 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法 // 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象 ... // 仅贴出核心代码 // 1. 通过 为Proxy类指定类加载器对象 & 一组interface,从而创建动态代理类 // >>关注3 Class cl = getProxyClass(loader, interfaces); // 2. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型 Constructor cons = cl.getConstructor(constructorParams); // 3. 通过动态代理类的构造函数 创建 代理类实例(传入调用处理器对象 return (Object) cons.newInstance(new Object[] { h }); // 特别注意 // 1. 动态代理类继承 Proxy 类 & 并实现了在Proxy.newProxyInstance()中提供的一系列接口(接口数组) // 2. Proxy 类中有一个映射表 // 映射关系为:(<ClassLoader>,(<Interfaces>,<ProxyClass>) ) // 即:1级key = 类加载器,根据1级key 得到 2级key = 接口数组 // 因此:1类加载器对象 1接口数组 = 确定了一个代理类实例 ... // 回到调用关注2的原处 } <-- 关注3:getProxyClass()源码分析 --> // 作用:创建动态代理类 public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces) throws IllegalArgumentException { ... // 仅贴出关键代码 <-- 1. 将目标类所实现的接口加载到内存中 --> // 遍历目标类所实现的接口 for (int i = 0; i < interfaces.length; i ) { // 获取目标类实现的接口名称 String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { // 加载目标类实现的接口到内存中 interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] " is not visible from class loader"); } } <-- 2. 生成动态代理类 --> // 根据传入的接口 & 代理对象 创建动态代理类的字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); // 根据动态代理类的字节码 生成 动态代理类 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } // 最终返回动态代理类 return proxyClass; } // 回到调用关注3的原处 3.2.1总结
3.2.2、(关注2)通过调用动态代理对象方法从而调用目标对象方法//使用代码 oneproxy.doOneThing("abd"); Interfaze oneproxy = JDKProxy.getProxy(new MyImplement()); System.out.println("oneProxy : " oneproxy); System.out.println(Proxy.class.isAssignableFrom(oneproxy.getClass())); //输出结果:oneProxy : class com.sun.proxy.$Proxy0 true // 这里关于isAssignableFrom()理解这个就可以 :父类.class.isAssignableFrom(子类.class) //发现oneproxy其实是proxy的子类 oneproxy.doOneThing("abd"); // 该方法的逻辑实际上是调用了父类Proxy类的h参数的invoke() // h参数即在Proxy.newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象 // 即调用了调用处理器的InvocationHandler.invoke() // 而复写的invoke()利用反射机制:Object result=method.invoke(proxied,args) // 从而调用目标对象的的方法 3.2.2总结
四、动态代理的实现:继承实现这里需要cglib- nodep -2.1.3.jar这个架包 4.1、具体使用public class CGLIBproxy { public CGLIBproxy() { } public static <T> T getProxy(Class<?> klass) { try { return getProxy(klass.newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } @SuppressWarnings("unchecked") public static <T> T getProxy(Object object) { Class<?> klass = object.getClass(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(klass); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result = null; System.out.println("前"); result = method.invoke(object, args); System.out.println("后"); return result; } }); return (T) enhancer.create(); } } Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。 4.2、方法介绍Callback-MethodInterceptor方法拦截器。这个东西和JDK自带的InvocationHandler很类似 Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable 这其中MethodProxy proxy参数一般是用来调用原来的对应方法的。比如可以 测试类: Complex twoProxy = CGLIBproxy.getProxy(Complex.class); twoProxy.setReal(10); twoProxy.setVir(-1.3); System.out.println(twoProxy); 五、总结接口实现: 继承实现: Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(klass); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result = null; System.out.println("前"); result = method.invoke(object, args); System.out.println("后"); return result; } }); 参考博文:设计模式:这是一份全面 & 清晰的动态代 理模式(Proxy Pattern)学习指南添加链接描述 来源:https://www./content-4-851001.html |
|