結合Spring框架,在進行數據庫操做的時候,常常會使用@Transactional註解,工做的經歷中看到不少人使用方式都是錯誤的,沒有深刻理解過其原理,其實這是很危險的!!本篇將深刻源碼分析@Transactional註解的工做原理。git
首先從tx:annotation-driven提及。配置了tx:annotation-driven,就一定有對應的標籤解析器類,查看NamespaceHandler接口的實現類,能夠看到一個TxNamespaceHandler,它註冊了AnnotationDrivenBeanDefinitionParser對annotation-driven元素進行解析。
github
進入AnnotationDrivenBeanDefinitionParser類,重點看parse方法。
從代碼中能夠看出,若是tx:annotation-driven中沒有配置mode參數,則默認使用代理模式進行後續處理;若是配置了mode=aspectj,則使用aspectj代碼織入模式進行後續處理。數據庫
本篇分析使用代理模式的代碼,進入AopAutoProxyConfigurer.configureAutoProxyCreator方法。
上圖代碼中標出了一行核心代碼,容易被忽略。進入AopNamespaceUtils.registerAutoProxyCreatorIfNecessary方法。
重點關注上圖中標出的代碼,進入AopConfigUtils.registerAutoProxyCreatorIfNecessary方法。
上圖中的代碼向Spring容器中註冊了一個InfrastructureAdvisorAutoProxyCreator類。可能會疑問爲何要註冊這個類,有什麼做用?查看InfrastructureAdvisorAutoProxyCreator類繼承關係。
經過上圖中的關係,能夠發現InfrastructureAdvisorAutoProxyCreator間接實現了BeanPostProcessor接口,從AbstractAutoProxyCreator類中繼承了postProcessAfterInitialization方法。Spring容器在初始化每一個單例bean的時候,會遍歷容器中的全部BeanPostProcessor實現類,並執行其postProcessAfterInitialization方法。框架
進入AbstractAutoProxyCreator類的postProcessAfterInitialization方法。
其中wrapIfNecessary方法是建立代理對象的核心方法。
getAdvicesAndAdvisorsForBean方法會遍歷容器中全部的切面,查找與當前實例化bean匹配的切面,這裏就是獲取事務屬性切面,查找@Transactional註解及其屬性值,具體實現比較複雜,這裏暫不深刻分析,最終會獲得BeanFactoryTransactionAttributeSourceAdvisor實例,而後根據獲得的切面進入createProxy方法,建立一個AOP代理。
進入ProxyFactory.getProxy方法。
createAopProxy方法決定使用JDK仍是Cglib建立代理。
能夠看出默認是使用JDK動態代理建立代理,若是目標類是接口,則使用JDK動態代理,不然使用Cglib。這裏分析使用JDK動態代理的方式,進入JdkDynamicAopProxy.getProxy方法。
能夠看到很熟悉的建立代理的代碼Proxy.newProxyInstance。這裏要注意的是,newProxyInstance方法的最後一個參數是JdkDynamicAopProxy類自己,也就是說在對目標類進行調用的時候,會進入JdkDynamicAopProxy的invoke方法。源碼分析
這裏只關注JdkDynamicAopProxy的invoke方法的重點代碼。
this.advised.getInterceptorsAndDynamicInterceptionAdvice獲取的是當前目標方法對應的攔截器,裏面是根據以前獲取到的切面來獲取相對應攔截器,這時候會獲得TransactionInterceptor實例。若是獲取不到攔截器,則不會建立MethodInvocation,直接調用目標方法。這裏使用TransactionInterceptor建立一個ReflectiveMethodInvocation實例,調用的時候進入ReflectiveMethodInvocation的proceed方法。
代碼中的interceptorOrInterceptionAdvice就是TransactionInterceptor的實例,執行invoke方法進入TransactionInterceptor的invoke方法。
post
TransactionInterceptor從父類TransactionAspectSupport中繼承了invokeWithinTransaction方法。
能夠看到,在須要進行事務操做的時候,Spring會在調用目標類的目標方法以前進行開啓事務、調用異常回滾事務、調用完成會提交事務。ui
是否須要開啓新事務,是根據@Transactional註解上配置的參數值來判斷的。若是須要開啓新事務,獲取Connection鏈接,而後將鏈接的自動提交事務改成false,改成手動提交。this
當對目標類的目標方法進行調用的時候,若發生異常將會進入completeTransactionAfterThrowing方法。
Spring並不會對全部類型異常都進行事務回滾操做,默認是隻對Unchecked Exception(Error和RuntimeException)進行事務回滾操做。
總結
從上面的分析能夠看到,Spring使用AOP實現事務的統一管理,爲開發者提供了很大的便利。可是,有部分開發人員會誤用這個便利,基本都是下面這兩種狀況:
1.A類的a1方法沒有標註@Transactional,a2方法標註@Transactional,在a1裏面調用a2;
2.將@Transactional註解標註在非public方法上。spa
第一種爲何是錯誤用法,緣由很簡單,a1方法是目標類A的原生方法,調用a1的時候即直接進入目標類A進行調用,在目標類A裏面只有a2的原生方法,在a1裏調用a2,即直接執行a2的原生方法,並不經過建立代理對象進行調用,因此並不會進入TransactionInterceptor的invoke方法,不會開啓事務。3d
@Transactional的工做機制是基於AOP實現的,而AOP是使用動態代理實現的,動態代理要麼是JDK方式、要麼是Cglib方式。若是是JDK動態代理的方式,根據上面的分析能夠知道,目標類的目標方法是在接口中定義的,也就是必須是public修飾的方法才能夠被代理。若是是Cglib方式,代理類是目標類的子類,理論上能夠代理public和protected方法,可是Spring在進行事務加強是否可以應用到當前目標類判斷的時候,遍歷的是目標類的public方法,因此Cglib方式也只對public方法有效。
深刻Class類getMethods方法,能夠看到取得是public修飾的方法。