IT忍者神龜之Java動態代理與CGLib代理,忍者神龜cglib
- <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是依靠什麼來判斷採用哪種Proxy 原則來產生AOP代理呢?以下代碼就是Spring的判斷邏輯
advisedSupport.isOptimize()與advisedSupport.isProxyTargetClass()預設返回都是false,所以在預設情況下目標對象有沒有實現介面決定著Spring採取的策略,當然可以設定advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回為true,這樣無論目標對象有沒有實現介面Spring都會選擇使用CGLIB代理。所以在預設情況下,如果一個目標對象如果實現了介面Spring則會選擇JDK動態Proxy 原則動態建立一個介面實作類別(動態代理類)來代理目標對象,可以通俗的理解這個動態代理類是目標對象的另外一個版本,所以這兩者之間在強制轉換的時候會拋出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。