哪些方法不能實施Spring AOP事務

未找到出處,作者發現請告訴我
    由於Spring事務管理是基於接口代理或動態字節碼技術,通過AOP實施事務增強的。雖然Spring還支持AspectJ LTW在類加載期實施增強,但這種方法很少使用,所以我們不予關注。 

    對於基於接口動態代理的AOP事務增強來說,由於接口的方法都必然是public的,這就要求實現類的實現方法也必須是public的(不能是protected、private等),同時不能使用static的修飾符。所以,可以實施接口動態代理的方法只能是使用“public”或“public final”修飾符的方法,其他方法不可能被動態代理,相應的也就不能實施AOP增強,換句話說,即不能進行Spring事務增強了。 
    基於CGLib字節碼動態代理的方案是通過擴展被增強類,動態創建其子類的方式進行AOP增強植入的。由於使用final、static、private修飾符的方法都不能被子類覆蓋,相應的,這些方法將無法實施AOP增強。所以方法簽名必須特別注意這些修飾符的使用,以免使方法不小心成爲事務管理的漏網之魚。 

事務增強遺漏實例 

   本節中,我們通過具體的實例說明基於CGLib字節碼動態代理無法享受Spring AOP事務增強的特殊方法。 
  
Java代碼  收藏代碼
  1. package com.baobaotao.special;  
  2. import org.springframework.stereotype.Service;  
  3. @Service("userService")  
  4. public class UserService {  
  5.   
  6.     //① private方法因訪問權限的限制,無法被子類覆蓋  
  7.     private void method1() {  
  8.         System.out.println("method1");  
  9.     }  
  10.   
  11.     //② final方法無法被子類覆蓋  
  12.     public final void method2() {  
  13.         System.out.println("method2");  
  14.     }  
  15.   
  16.     //③ static是類級別的方法,無法被子類覆蓋  
  17.     public static void method3() {  
  18.         System.out.println("method3");  
  19.     }  
  20.   
  21.     //④ public方法可以被子類覆蓋,因此可以被動態字節碼增強  
  22.     public void method4() {  
  23.         System.out.println("method4");  
  24.     }   
  25. }  


   Spring通過CGLib動態代理技術對UserService Bean實施AOP事務增強的關鍵配置,具體如下所示: 

Xml代碼  收藏代碼
  1. …  
  2.     <aop:config proxy-target-class="true"><!-- ①顯式使用CGLib動態代理 -->  
  3.   
  4.         <!-- ②希望對UserService所有方法實施事務增強 -->  
  5.         <aop:pointcut id="serviceJdbcMethod"  
  6.                       expression="execution(* com.baobaotao.special.UserService.*(..))"/>  
  7.         <aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/>  
  8.     </aop:config>  
  9.     <tx:advice id="jdbcAdvice" transaction-manager="jdbcManager">  
  10.         <tx:attributes>  
  11.             <tx:method name="*"/>  
  12.         </tx:attributes>  
  13.     </tx:advice>  
  14. …  


   在①處,我們通過proxy-target-class="true"顯式使用CGLib動態代理技術,在②處通過AspjectJ切點表達式表達UserService所有的方法,希望對UserService所有方法都實施Spring AOP事務增強。 
   在UserService添加一個可執行的方法,如下所示: 
Java代碼  收藏代碼
  1. package com.baobaotao.special;  
  2. import org.springframework.context.ApplicationContext;  
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  4. import org.springframework.stereotype.Service;  
  5.   
  6. @Service("userService")  
  7. public class UserService {  
  8.     …  
  9.     public static void main(String[] args) {  
  10.         ApplicationContext ctx =   
  11.              new ClassPathXmlApplicationContext("user/special/applicationContext.xml");  
  12.         UserService service = (UserService) ctx.getBean("userService");  
  13.   
  14.         System.out.println("before method1");  
  15.         service.method1();  
  16.         System.out.println("after method1");  
  17.   
  18.         System.out.println("before method2");  
  19.         service.method2();  
  20.         System.out.println("after method2");  
  21.   
  22.         System.out.println("before method3");  
  23.         service.method3();  
  24.         System.out.println("after method3");  
  25.   
  26.         System.out.println("before method4");  
  27.         service.method4();  
  28.         System.out.println("after method4");  
  29.   
  30.     }  
  31. }  


   在運行UserService之前,將Log4J日誌級別設置爲DEBUG,運行以上代碼查看輸出日誌,如下所示: 
引用

①未啓用事務 
before method1 
in method1 
after method1 

②未啓用事務 
before method2 
in method2 
after method2 

③未啓用事務 
before method3 
in method3 
after method3 

④啓用事務 
before method4 
Creating new transaction with name [com.baobaotao.special.UserService.method4]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 
… 
in method4 
Initiating transaction commit 
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] 
Releasing JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] after transaction 
Returning JDBC Connection to DataSource 
after method4 

⑤未用事務 
before method5 
in method5 
after method5 

⑥啓用事務 
before method6 
Creating new transaction with name [com.baobaotao.special.UserService.method6]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 
… 
in method6 
Initiating transaction commit 
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] 
… 
after method6 


  觀察以上輸出日誌,很容易發現method1~method3及method5這4個方法都沒有被實施Spring的事務增強,而method4和method6被實施了事務增強。這個結果驗證了我們前面的論述。 
  我們通過下表描述哪些特殊方法將成爲Spring AOP事務增強的漏網之魚。 
序    號 動態代理策略 不能被事務增強的方法
1 基於接口的動態代理 除public外的其他所有的方法,此外public static也不能被增強
2 基於CGLib的動態代理 private、static、final的方法


   不過,需要特別指出的是,這些不能被Spring事務增強的特殊方法並非就不工作在事務環境下。只要它們被外層的事務方法調用了,由於Spring事務管理的傳播級別,內部方法也可以工作在外部方法所啓動的事務上下文中。我們說,這些方法不能被Spring進行AOP事務增強,是指這些方法不能啓動事務,但是外層方法的事務上下文依舊可以順利地傳播到這些方法中。 
   這些不能被Spring事務增強的方法和可被Spring事務增強的方法唯一的區別在於“是否可以主動啓動一個新事務”:前者不能而後者可以。對於事務傳播行爲來說,二者是完全相同的,前者也和後者一樣不會造成數據連接的泄漏問題。換句話說,如果這些“特殊方法”被無事務上下文的方法調用,則它們就工作在無事務上下文中;反之,如果被具有事務上下文的方法調用,則它們就工作在事務上下文中。 
   對於private的方法,由於最終都會被public方法封裝後再開放給外部調用,而public方法是可以被事務增強的,所以基本上沒有什麼問題。在實際開發中,最容易造成隱患的是基於CGLib的動態代理時的“public static”和“public final”這兩種特殊方法。原因是它們本身是public的,因此可以直接被外部類(如Web層的Controller類)調用,只要調用者沒有事務上下文,這些特殊方法也就以無事務的方式運作。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章