总而言之,就是
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
CGlib比JDK快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
切面编程是Spring中非常重要的一个模块,切面编程的实现原理是动态代理,那么动态代理又有两种实现方式,一种方法是直接实现JDK中的InvocationHandler
接口,另一种方法是继承CGLIB。
那么问题来了,这两种方法有啥区别呢,分别适用于什么情况呢?
首先如果不是很清楚两者的区别的话,记住一般情况下InvocationHandler
要比CGLIB要好就行了。
如果目标对象的代理至少实现了一个接口,那么就用JDK动态代理,所有由目标对象实现的接口将全部都被代理。如果目标对象没有实现任何接口,那么就用CGLIB代理。
但是如果非要使用CGLIB的话,那么CGLIB可能有下面的问题:
-
刚才提到了,
InvocationHandler
是实现的接口,而CGLIB则是继承的父类,那么由于继承的限制,如果父类中有final的成员,那么是继承不到的。 -
还有从Spring 3.2以后不再将CGLIB放在项目的classpath下,而是将CGLIB类打包放在spring-core下面的org.springframework中。这个就意味着基于CGLIB的动态代理与JDK的动态代理在支持“just works”就一样了。
-
在Spring 4.0中,因为CGLIB代理实例是通过Objenesis创建的,所以代理对象的构造器不再有两次调用。
想要强制使用CGLIB,那么就设置<aop:config>
下面的proxy-target-class
属性为true
:
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>
- 1
- 2
- 3
要是使用@AspectJ强制使用CGLIB的话,可以配置<aop:aspectj-autoproxy>
下的proxy-target-class
属性为true
:
<aop:aspectj-autoproxy proxy-target-class="true"/>