Spring AOP部分使用JDK動態代理或者CGLIB來為目標對象創建代理。(建議盡量使用JDK的動態代理)
如果被代理的目標對象實現了至少一個接口,則會使用JDK動態代理。所有該目標類型實現的接口都將被代理。若該目標對象沒有實現任何接口,則創建一個CGLIB代理。
如果你希望強制使用CGLIB代理,(例如:希望代理目標對象的所有方法,而不只是實現自接口的方法)那也可以。但是需要考慮以下問題:
無法通知(advise)Final 方法,因為他們不能被覆寫。
你需要將CGLIB 2二進制發行包放在classpath下面,與之相較JDK本身就提供了動態代理
強制使用CGLIB代理需要將 |aop:config| 的 proxy-target-class 屬性設為true:
|aop:config proxy-target-class="true"|
...
|/aop:config|
當需要使用CGLIB代理和@AspectJ自動代理支持,請按照如下的方式設置 |aop:aspectj-autoproxy| 的 proxy-target-class 屬性:
|aop:aspectj-autoproxy proxy-target-class="true"/|
而實際使用的過程中才會發現細節問題的差別,The devil is in the detail.JDK動態代理:其代理對象必須是某個接口的實現,它是通過在運行期間創建一個接口的實現類來完成對目標對象的代理。
CGLIB代理:實現原理類似於JDK動態代理,只是它在運行期間生成的代理對象是針對目標類擴展的子類。CGLIB是高效的代碼生成包,底層是依靠ASM(開源的java字節碼編輯類庫)操作字節碼實現的,性能比JDK強。
Spring是依靠什么來判斷采用哪種代理策略來生成AOP代理呢?以下代碼就是Spring的判斷邏輯
//org.springframework.aop.framework.DefaultAopProxyFactory
//參數AdvisedSupport 是Spring AOP配置相關類
public AopProxy createAopProxy(AdvisedSupport advisedSupport)
throws AopConfigException {
//在此判斷使用JDK動態代理還是CGLIB代理
if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()
|| hasNoUserSuppliedProxyInterfaces(advisedSupport)) {
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. "
+ "Add CGLIB to the class path or specify proxy interfaces.");
}
return CglibProxyFactory.createCglibProxy(advisedSupport);
} else {
return new JdkDynamicAopProxy(advisedSupport);
}
}
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代理。
如果當需要使用CGLIB代理和@AspectJ自動代理支持,請按照如下的方式設置 |aop:aspectj-autoproxy| 的 proxy-target-class 屬性:
|aop:aspectj-autoproxy proxy-target-class="true"/|
這樣使用CGLIB代理也就不會出現前面提到的ClassCastException問題了,也可以在性能上有所提高,關鍵是對於代理對象是否繼承接口可以統一使用。