JDK
JDK 动态代理通过回调拦截方式,通过反射获取模板接口名字、内部方法以及参数,再原来的接口上修改,拼接,产生一个新的java代理对象(类似于mybatis的反序列化代码过程)
1.拼接java源代码
2.编译为class文件
3.类加载器加载新的class到内存中
4.通过反射执行方法(就是在invoke()那个方法)
重点:生成的代理对象不能直接调用被代理对象的方法,而是通过反射,等于说每次都得用反射调用一次,所以执行效率不高。
Cglib
Cglib动态代理采用继承方式,底层基于asm字节码技术参数一个新的java代理对象
1.生成class文件
2.读取class文件到内存中
3.采用fastclass索引机制执行方法(关键在于invoke()那个方法)
重点:Cglib代理实际上是通过继承,也就是生成一个继承被代理对象的类,编译成class文件时还会额外生成一个fastclass文件,该文件记录各个method的class索引(类名+方法名+参数),当执行某个方法时,通过计算索引,定位到具体的方法,代理对象执行该方法,然后super调用父类(执行了被代理对象的方法)。
生成代理对象时通过fastclass索引机制直接定位到被代理对象的class文件,从而实现反复调用,等于说是class复用,每次都是直接拿被代理对象的class内容执行的。
对比
都是需要再原来的被代理对象基础上加额外代码。前者的invoke内部通过反射,后者通过fastclass索引机制,后者实现执行效率更高(高于反射数倍)。
从最终生成的被代理对象来看:
jdk通过拼接,内部只需要一个method.invoke()就可以达到目标。
cglib则需要将被代理对象(可以是多个)的class文件通过集合形式定位好,之后先执行增强代码,之后通过回调对象来执行目标对象。
总结:
也就是说jdk动态代理生成类速度快,调用慢,cglib生成类速度慢,但后续调用快,在老版本CGLIB的速度是JDK速度的10倍左右,但是CGLIB启动类比JDK慢8倍左右,但是实际上JDK的速度在版本升级的时候每次都提高很多性能,而CGLIB仍止步不前.
在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
为何框架大多数通过接口形式?
接口形式结合多态更加方便,而且接口本身没有什么实质性代码,jdk 动态代理生成的对象,实际上执行时没有“执行被代理方法”这一步骤,而jdk生成class速度比较快(相对cglib更加轻量级),随着jdk的版本变化对反射这一块进行了优化,jdk更加快捷。