本文共 12792 字,大约阅读时间需要 42 分钟。
在讨论动态代理前,我们先回想一下java的反射机制。因为动态代理是会用到反射的,而且java的反射不仅使用广泛,而且功能强大。
当我们使用new
关键字创建对象时,虚拟机会先查看对象所属的类是否被加载到内存中,如果没有,就会先通过类的全限定类名来加载,当加载并初始化完成后,再进行对象的创建工作。当一个类文件被类加载器加载到虚拟机中,虚拟机会读取文件信息,并生成一个该类的Class对象。因此,如果我们可以直接操作虚拟机堆中的Class对象,那么就不需要new
来创建对象。这种思想就是反射。
反射支持的方法有多种,主要的有:
获取Class对象:
对Class对象进行操作:
对CLass对象类名进行操作:
对Class对象修饰符进行操作:
对Class对象包进行操作:
对CLass对象继承的类/实现的接口进行操作:
对Class对象构造函数进行操作:
NoSuchMethodException
异常)对Class方法进行操作:
null
值),第二个参数是参数数组。对Class字段进行操作:
jdk的静态代理就不用说了,就是创建一个接口,然后让目标类和代理类都实现这个接口,并且它们都有一个接收代理接口的构造参数,然后用的时候就创建目标类,然后把目标类传入代理类,以后操作就用代理类就可以了。这样后期修改代理代码就只要修改代理类一处就好了。具体代码实现如下:
public class StaticProxy { public static void main(String[] args) { TargetClass targetClass = new TargetClass(); ProxyClass proxyClass = new ProxyClass(targetClass); proxyClass.doSomething(); }}interface Proxy{ public void doSomething();}class TargetClass implements Proxy{ @Override public void doSomething() { System.out.println("我是目标类"); }}class ProxyClass implements Proxy{ private TargetClass targetClass; public ProxyClass(TargetClass targetClass) { this.targetClass = targetClass; } @Override public void doSomething() { System.out.println("我是代理类"); targetClass.doSomething(); }}
由以上代码我们可以看出静态代理的大体架构如下所示:
静态代理的实现原理很简单,在代理类少的情况下可以这样做,但是当代理规模一上来,不仅要写诸多的代理类,而且要注意代理类之间的关系,可能有的代理类代理的是代理类的代理的代理类。。。(有一种用为所欲为成语接龙的感觉)。这个时候,就要隆重推出反射使用中最靓的仔——jdk动态代理。
jdk动态代理不止需要目标类和代理类,它还需要一个中间类来通过调用invoke()方法来执行目标类的方法,动态代理的大体架构如下所示:
jdk动态代理的代码示例如下:
public class DynamicProoxyByJLR { public static void main(String[] args) { // 注意:旧版的jdk应该使用 //System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); TargetClass targetClass = new TargetClass(); Class aClass = targetClass.getClass(); MyProxy myProxy = (MyProxy) Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), new ProxyClass(targetClass)); myProxy.doSomething(); }}interface MyProxy{ public void doSomething();}class TargetClass implements MyProxy{ @Override public void doSomething() { System.out.println("我是目标类"); }}class ProxyClass implements InvocationHandler { private MyProxy target; public ProxyClass(MyProxy target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我是动态代理中间类"); // 通过反射调用目标类的方法 method.invoke(target, args); return null; }}
当执行完该程序后,我们可以看到在项目的当前目录会生成一个新的文件夹,该文件夹中就有jdk动态代理生成的Proxy0.class文件(我的路径是项目路径/cn/bigkai/proxy
),虚拟机就是加载这个Class文件来进行动态代理的。我们通过反编译器来看看这个Class文件的内容:
// 篇幅有限,删除了部分不重要的内容package cn.bigkai.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;// 该类继承Proxy类,实现了MyProxy接口final class $Proxy0 extends Proxy implements MyProxy { private static Method m1; private static Method m3; private static Method m2; private static Method m0; static { try { // 通过反射获取方法对象,从下面可以看出不仅需要获取代理方法,而且要获取equals、toString、hashCode方法。 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{ Class.forName("java.lang.Object")}); m3 = Class.forName("cn.bigkai.proxy.MyProxy").getMethod("doSomething", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } public $Proxy0(InvocationHandler paramInvocationHandler) throws { // 将paramInvocationHandler传递给Proxy的h属性保存起来 super(paramInvocationHandler); } public final void doSomething() throws { try { // 这里调用$Proxy0(InvocationHandler paramInvocationHandler)传递给父类h的invoke方法 // this表示当前代理类,m3表示doSomething方法的全类名,null是方法的参数(没有参数就是null) this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } }}
对以上代码分析我们可以看出,jdk的动态代理其实就是实现InvocationHandler的拦截器,通过反射机制生成代理接口的$Proxy0类,在调用具体方法前调用InvokeHandler来处理,传入指定的当前类、方法、方法参数来执行。
利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来实现动态代理的框架。cglib不同于jdk 动态代理通过接口实现来实现,它是针对类实现代理的,主要是对指定的类生成一个子类,实现指定的方法增强,因为是采用继承的方式,所以对于final修饰的类或方法,是无法继承的。
下面我们先来看看cglib的入门实例:
public class DynamicProxyByCglib { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\JAVACODE\\flink-demo"); // 创建Enhancer对象,相当于代理类的创建工厂 Enhancer enhancer = new Enhancer(); // 设置目标类的字节码文件 enhancer.setSuperclass(TargetClass.class); // 设置拦截器作为回调函数 enhancer.setCallback(new MyMethodInterceptor()); // 创建代理类 TargetClass proxyClass = (TargetClass)enhancer.create(); proxyClass.doSomething(); }}class TargetClass { public void doSomething(){ System.out.println("我是目标类"); }}class MyMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("我是动态代理中间类"); return methodProxy.invokeSuper(o, objects); }}
当执行完程序后,在指定目录下会出现这三个文件:
我们主要来看一下TargetClass E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB99529e9文件,因为它是真正执行代理类方法的:
// 继承目标类TargetClass,实现Factory接口,Factory的作用是设置属性和工厂式创建实例化对象public class TargetClass$$EnhancerByCGLIB$$99529e9 extends TargetClass implements Factory{ private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback CGLIB$STATIC_CALLBACKS[]; private MethodInterceptor CGLIB$CALLBACK_0; private static final Object CGLIB$emptyArgs[]; private static Object CGLIB$CALLBACK_FILTER; // 一个方法对应一个Method类型,一个MethodProxy类型 private static final Method CGLIB$doSomething$0$Method; private static final MethodProxy CGLIB$doSomething$0$Proxy; private static final Method CGLIB$equals$1$Method; private static final MethodProxy CGLIB$equals$1$Proxy; private static final Method CGLIB$toString$2$Method; private static final MethodProxy CGLIB$toString$2$Proxy; private static final Method CGLIB$hashCode$3$Method; private static final MethodProxy CGLIB$hashCode$3$Proxy; private static final Method CGLIB$clone$4$Method; private static final MethodProxy CGLIB$clone$4$Proxy; static { CGLIB$STATICHOOK1(); } public TargetClass$$EnhancerByCGLIB$$99529e9() { CGLIB$BIND_CALLBACKS(this); } // 对静态属性赋值 static void CGLIB$STATICHOOK1() { // 这两个数组用来保存反射获取的Method对象 Method amethod[]; Method amethod1[]; CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; // 获取目标类的字节码文件 Class class1 = Class.forName("cn.bigkai.proxy.TargetClass$$EnhancerByCGLIB$$99529e9"); // 代理类的字节码文件 Class class2; // ReflectUtils是一个包装各种反射操作的工具类,通过这个工具类来获取各个方法的Method对象,然后保存到上述的Method数组中 amethod = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods()); Method[] _tmp = amethod; // 为目标类的每一个方法都建立索引,方便查找(FastClass机制) CGLIB$equals$1$Method = amethod[0]; CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1"); CGLIB$toString$2$Method = amethod[1]; CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2"); CGLIB$hashCode$3$Method = amethod[2]; CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3"); CGLIB$clone$4$Method = amethod[3]; CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4"); amethod1 = ReflectUtils.findMethods(new String[] { "doSomething", "()V" }, (class2 = Class.forName("cn.bigkai.proxy.TargetClass")).getDeclaredMethods()); Method[] _tmp1 = amethod1; CGLIB$doSomething$0$Method = amethod1[0]; CGLIB$doSomething$0$Proxy = MethodProxy.create(class2, class1, "()V", "doSomething", "CGLIB$doSomething$0"); } final void CGLIB$doSomething$0() { super.doSomething(); } public final void doSomething() { CGLIB$CALLBACK_0; if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1 // 如果方法拦截器Wie空,就返回_L1: JVM INSTR pop ; CGLIB$BIND_CALLBACKS(this); CGLIB$CALLBACK_0;_L2: JVM INSTR dup ; JVM INSTR ifnull 37; goto _L3 _L4_L3: break MISSING_BLOCK_LABEL_21;_L4: break MISSING_BLOCK_LABEL_37; this; CGLIB$doSomething$0$Method; CGLIB$emptyArgs; CGLIB$doSomething$0$Proxy; intercept(); // 执行拦截器方法 return; super.doSomething(); return; }}
FastClass机制:我们知道使用反射调用方法的效率比new慢,因此cglib提供了FastClass机制,该机制是直接new一个新的实例类,然后对该实例类的方法标记索引,在代理时通过索引直接调用对应的方法。FastClass调用方法如下所示:
public class DynamicProxyByCglib { public static void main(String[] args) throws InvocationTargetException { // FastClass创建子实例 FastClass fastClass = FastClass.create(TargetClass.class); // 创建目标类实例 TargetClass fastClassInterface = (TargetClass)fastClass.newInstance(new Class[]{ String.class}, new Object[]{ "zhangsan"}); // 调用委托类方法 fastClass.invoke("say", new Class[]{ String.class}, fastClassInterface, new Object[]{ "李四"}); }} class TargetClass { private String name; public TargetClass() { } public TargetClass(String name) { this.name = name; } public void doSomething(){ System.out.println("我是目标类"); } public void say(String name){ System.out.println(name + " 你好,我是 " + this.name); }}
当我们创建了Enhance对象,设置了相关属性并实例化了代理类后,调用代理类的doSomething方法,此时代码的执行流程如下:
进入方法拦截器,调用intercept方法。
调用intercept方法内的invokeSuper方法,进入invokeSuper代码内。
调用invokeSuper方法内的init方法,init方法为目标类的方法建立索引。
init方法源码如下:
当调用init方法获取FastClass后,就调用代理类的方法执行:
调用代理类的invoke方法执行,实际上就是调用了目标类的对应方法:
Cglib | JLR | |
---|---|---|
是否提供子类代理 | 是 | 否 |
是否提供接口代理 | 是(可强制) | 是 |
jdk1.6及以下两者效率 | 比JLR要高 | 比cglib低 |
jdk1.6到jdk1.8两者效率 | 在调用次数多时高于JLR | 在调用次数少时高于cglib |
jdk1.8及以上两者消息 | 低于JLR | 高于cglib |
区别 | 通过继承类来实现代理,不能继承被final修饰的类和属性 | 实现InvocationHandler使用Proxy.newProxyInstance产生代理对象,调用invoke方法实现方法增强 |
转载地址:http://yjqgn.baihongyu.com/