Sring如何选择JDK动态代理与CGLIB字节码加强

Spring将事务代理工厂TransactionProxyFactoryBean或自动代理拦截器BeanNameAutoProxyCreator的proxyTargetClass属性,设置为true,则使用CGLIB代理,此属性默认为false,使用JDK动态代理。java

Spring AOP部分使用JDK动态代理或者CGLIB来为目标对象建立代理。(建议尽可能使用JDK的动态代理)spring

若是被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。全部该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则建立一个CGLIB代理。
若是你但愿强制使用CGLIB代理,(例如:但愿代理目标对象的全部方法,而不仅是实现接口的方法)那也能够,可是须要考虑如下问题:
1.没法通知(advise)Final 方法,由于他们不能被覆写。 
2.你须要将CGLIB 2二进制发行包放在classpath下面,与之相较JDK自己就提供了动态代理 
3.强制使用CGLIB代理须要将 |aop:config| 的 proxy-target-class 属性设为true:性能

<aop:config proxy-target-class="true">
...
</aop:config>

当须要使用CGLIB代理和@AspectJ自动代理支持,请按照以下的方式设置 <aop:aspectj-autoproxy>的 proxy-target-class 属性:spa

<aop:aspectj-autoproxy proxy-target-class="true"/>

而实际使用的过程当中才会发现细节问题的差异,The devil is in the detail.代理

JDK动态代理:其代理对象必须是某个接口的实现,它是经过在运行期间建立一个接口的实现类来完成对目标对象的代理。
CGLIB代理:实现原理相似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操做字节码实现的,性能比JDK强。
Spring是依靠什么来判断采用哪一种代理策略来生成AOP代理呢?如下代码就是Spring的判断逻辑  对象

advisedSupport.isOptimize()与advisedSupport.isProxyTargetClass()默认返回都是false,因此在默认状况下目标对象有没有实现接口决定着Spring采起的策略,固然能够设置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回为true,这样不管目标对象有没有实现接口Spring都会选择使用CGLIB代理。

因此在默认状况下,若是一个目标对象若是实现了接口Spring则会选择JDK动态代理策略动态的建立一个接口实现类(动态代理类)来代理目标对象,能够通俗的理解这个动态代理类是目标对象的另一个版本,因此这二者之间在强制转换的时候会抛出java.lang.ClassCastException。而因此在默认状况下,若是目标对象没有实现任何接口,Spring会选择CGLIB代理, 其生成的动态代理对象是目标类的子类。

上说的是默认状况下,也能够手动配置一些选项使Spring采用CGLIB代理。 
org.springframework.transaction.interceptor.TransactionProxyFactoryBean是org.springframework.aop.framework. ProxyConfig的子类,因此能够参照ProxyConfig里的一些设置以下所示,将optimize和proxyTargetClass任意一个设置为true均可以强制Spring采用CGLIB代理。blog

若是当须要使用CGLIB代理和@AspectJ自动代理支持,请按照以下的方式设置<aop:aspectj-autoproxy> 的 proxy-target-class 属性: 
<aop:aspectj-autoproxy proxy-target-class="true"/>继承

这样使用CGLIB代理也就不会出现前面提到的ClassCastException问题了,也能够在性能上有所提升,关键是对于代理对象是否继承接口均可以统一使用CGLIB。接口

相关文章
相关标签/搜索