Spring的事務管理難點剖析(6):特殊方法成漏網之魚


   因爲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

package com.baobaotao.special; import org.springframework.stereotype.Service; @Service("userService") public class UserService { //① private方法因訪問權限的限制,沒法被子類覆蓋 private void method1() { System.out.println("method1"); } //② final方法沒法被子類覆蓋 public final void method2() { System.out.println("method2"); } //③ static是類級別的方法,沒法被子類覆蓋 public static void method3() { System.out.println("method3"); } //④ public方法能夠被子類覆蓋,所以能夠被動態字節碼加強 public void method4() { System.out.println("method4"); } }

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

… <aop:config proxy-target-class="true"><!-- ①顯式使用CGLib動態代理 --> <!-- ②但願對UserService全部方法實施事務加強 --> <aop:pointcut id="serviceJdbcMethod" expression="execution(* com.baobaotao.special.UserService.*(..))"/> <aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/> </aop:config> <tx:advice id="jdbcAdvice" transaction-manager="jdbcManager"> <tx:attributes> <tx:method name="*"/> </tx:attributes> </tx:advice> …

在①處,咱們經過proxy-target-class="true"顯式使用CGLib動態代理技術,在②處經過AspjectJ切點表達式表達UserService全部的方法,但願對UserService全部方法都實施Spring AOP事務加強。
   在UserService添加一個可執行的方法,以下所示:spring

package com.baobaotao.special; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; @Service("userService") public class UserService { … public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("user/special/applicationContext.xml"); UserService service = (UserService) ctx.getBean("userService"); System.out.println("before method1"); service.method1(); System.out.println("after method1"); System.out.println("before method2"); service.method2(); System.out.println("after method2"); System.out.println("before method3"); service.method3(); System.out.println("after method3"); System.out.println("before method4"); service.method4(); System.out.println("after method4"); } }


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

引用

①未啓用事務
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類)調用,只要調用者沒有事務上 下文,這些特殊方法也就以無事務的方式運做。

注:以上內容摘自《Spring 3.x企業應用開發實戰》
相關文章
相關標籤/搜索