- <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>
測試代碼
- ApplicationContext ctx =
- <br> new FileSystemXmlApplicationContext("applicationContext.xml");
- <br> UserDAOImpl userDAOImpl =
- <br> (UserDAOImpl)ctx.getBean("userDAOProxy");
- <br> userDAOImpl.save();
上面這種情況下程序可以正常運行,但是如果UserDAOImpl實現了一個接口,其他不變
- 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的判斷邏輯
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代理。
- //相關配置,省略了一些不相關內容
- <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。
文章列表