- <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。