Java 代碼
- <br>public class UserDAOImpl{
- <br><br> public void save() {
- <br> // TODO Auto-generated method stub
- <br> System.out.println("user saved");
- <br> }
- <br>}
- <br>// 相關配置,省略了一些不相關內容
- <br><bean id="userDAO" class="UserDAOImpl">
- <br><bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
- <br> <property name="target">
- <br> <ref local="userDAO" />
- <br> </property>
- <br></bean>
測試代碼
Java 代碼
- ApplicationContext ctx =
- <br> new FileSystemXmlApplicationContext("applicationContext.xml");
- <br> UserDAOImpl userDAOImpl =
- <br> (UserDAOImpl)ctx.getBean("userDAOProxy");
- <br> userDAOImpl.save();
上面這種情況下程序可以正常運行,但是如果UserDAOImpl 實現了一個接口,其他不變
Java 代碼
- public class UserDAOImpl implements UserDAO {
- <br>
- <br> public void save() {
- <br> // TODO Auto-generated method stub
- <br> System.out.println("user saved");
- <br> }
- <br>
- <br>}
這種情況下,程序將不能正常運行,會拋出java.lang.ClassCastException 異常
理解上面這種情況產生的原因需要了解Spring AOP
的實現原理。
Spring
實現AOP
是依賴JDK
動態代理和CGLIB
代理實現的。
以下是JDK
動態代理和CGLIB
代理簡單介紹
JDK
動態代理:其代理對象必須是某個接口的實現,它是通過在運行期間創建一個接口的實現類來完成對目標對象的代理。
CGLIB
代理:實現原理類似於JDK
動態代理,只是它在運行期間生成的代理對象是針對目標類擴展的子類。CGLIB
是高效的代碼生成包,底層是依靠ASM
(開源的java
字節碼編輯類庫)操作字節碼實現的,性能比JDK
強。
Spring
是依靠什麼來判斷採用哪種代理策略來生成AOP
代理呢?以下代碼就是Spring
的判斷邏輯
Java 代碼
- //org.springframework.aop.framework.DefaultAopProxyFactory
- <br> // 參數AdvisedSupport 是Spring AOP 配置相關類
- <br> public AopProxy createAopProxy(AdvisedSupport advisedSupport)
- <br> throws AopConfigException {
- <br> // 在此判斷使用JDK 動態代理還是CGLIB 代理
- <br> if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()
- <br> || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {
- <br> if (!cglibAvailable) {
- <br> throw new AopConfigException(
- <br> "Cannot proxy target class because CGLIB2 is not available. "
- <br> + "Add CGLIB to the class path or specify proxy interfaces.");
- <br> }
- <br> return CglibProxyFactory.createCglibProxy(advisedSupport);
- <br> } else {
- <br> return new JdkDynamicAopProxy(advisedSupport);
- <br> }
- <br> }
advisedSupport.isOptimize() 與advisedSupport.isProxyTargetClass() 默認返回都是false ,所以在默認情況下目標對象有沒有實現接口決定着Spring 採取的策略,當然可以設置advisedSupport.isOptimize() 或者advisedSupport.isProxyTargetClass() 返回爲true ,這樣無論目標對象有沒有實現接口Spring 都會選擇使用CGLIB 代理。所以在默認情況下,如果一個目標對象如果實現了接口Spring 則會選擇JDK 動態代理策略動態的創建一個接口實現類(動態代理類)來代理目標對象,可以通俗的理解這個動態代理類是目標對象的另外一個版本,所以這兩者之間在強制轉換的時候會拋出j ava.lang.ClassCastException 。而所以在默認情況下,如果目標對象沒有實現任何接口,Spring 會選擇CGLIB 代理, 其生成的動態代理對象是目標類的子類。
以上說的是默認情況下,也可以手動配置一些選項使 Spring 採用 CGLIB 代理。
org.springframework.transaction.interceptor.TransactionProxyFactoryBean 是 org.springframework.aop.framework. ProxyConfig 的子類,所以可以參照 ProxyConfig 裏的一些設置如下所示,將 optimize 和 proxyTargetClass 任意一個設置爲 true 都可以強制 Spring 採用 CGLIB 代理。
Java 代碼
- // 相關配置,省略了一些不相關內容
- <br><bean id="userDAO" class="UserDAOImpl">
- <br><bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
- <br> <property name="target">
- <br> <ref local="userDAO" />
- <br> </property>
- <br> <property name="optimize">
- <br> <value>true</value>
- <br> </property>
- <br> <property name="proxyTargetClass">
- <br> <value>true</value>
- <br> </property>
- <br></bean>
使用 CGLIB 代理也就不會出現前面提到的ClassCastException 問題了,
也可以在性能上有所提高,但是也有它的弊端, Spring doc 原文解釋如下 optimization will usually mean that advice changes won't take effect after a proxy has been created. For this reason, optimization is disabled by default 。