<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring 事務(wù)底層原理

          共 6574字,需瀏覽 14分鐘

           ·

          2022-12-17 13:34

          • @EnableTransactionManagement工作原理
          • Spring事務(wù)基本執(zhí)行原理
          • Spring事務(wù)詳細執(zhí)行流程
          • Spring事務(wù)傳播機制
          • Spring事務(wù)傳播機制分類
            • 案例分析
            • 情況1
            • 情況2
            • 情況3
            • 情況4
          • Spring事務(wù)強制回滾
          • TransactionSynchronization


          一、@EnableTransactionManagement工作原理


          開啟Spring事務(wù)本質(zhì)上就是增加了一個Advisor,但我們使用@EnableTransactionManagement注解來開啟Spring事務(wù)是,該注解代理的功能就是向Spring容器中添加了兩個Bean:

          • AutoProxyRegistrar
          • ProxyTransactionManagementConfiguration

          AutoProxyRegistrar主要的作用是向Spring容器中注冊了一個InfrastructureAdvisorAutoProxyCreator的Bean。而InfrastructureAdvisorAutoProxyCreator繼承了AbstractAdvisorAutoProxyCreator,所以這個類的主要作用就是開啟自動代理的作用,也就是一個BeanPostProcessor,會在初始化后步驟中去尋找Advisor類型的Bean,并判斷當(dāng)前某個Bean是否有匹配的Advisor,是否需要利用動態(tài)代理產(chǎn)生一個代理對象。

          ProxyTransactionManagementConfiguration是一個配置類,它又定義了另外三個bean:

          • BeanFactoryTransactionAttributeSourceAdvisor:一個Advisor
          • AnnotationTransactionAttributeSource:相當(dāng)于BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut
          • TransactionInterceptor:相當(dāng)于BeanFactoryTransactionAttributeSourceAdvisor中的Advice

          AnnotationTransactionAttributeSource就是用來判斷某個類上是否存在@Transactional注解,或者判斷某個方法上是否存在@Transactional注解的。

          TransactionInterceptor就是代理邏輯,當(dāng)某個類中存在@Transactional注解時,到時就產(chǎn)生一個代理對象作為Bean,代理對象在執(zhí)行某個方法時,最終就會進入到TransactionInterceptor的invoke()方法。


          二、Spring事務(wù)基本執(zhí)行原理


          一個Bean在執(zhí)行Bean的創(chuàng)建生命周期時,會經(jīng)過InfrastructureAdvisorAutoProxyCreator的初始化后的方法,會判斷當(dāng)前Bean對象是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配邏輯為判斷該Bean的類上是否存在@Transactional注解,或者類中的某個方法上是否存在@Transactional注解,如果存在則表示該Bean需要進行動態(tài)代理產(chǎn)生一個代理對象作為Bean對象。

          該代理對象在執(zhí)行某個方法時,會再次判斷當(dāng)前執(zhí)行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配則執(zhí)行該Advisor中的TransactionInterceptor的invoke()方法,執(zhí)行基本流程為:

          1. 利用所配置的PlatformTransactionManager事務(wù)管理器新建一個數(shù)據(jù)庫連接
          2. 修改數(shù)據(jù)庫連接的autocommit為false
          3. 執(zhí)行MethodInvocation.proceed()方法,簡單理解就是執(zhí)行業(yè)務(wù)方法,其中就會執(zhí)行sql
          4. 如果沒有拋異常,則提交
          5. 如果拋了異常,則回滾


          三、Spring事務(wù)詳細執(zhí)行流程


          Spring事務(wù)執(zhí)行流程圖:https://www.processon.com/view/link/5fab6edf1e0853569633cc06

          四、Spring事務(wù)傳播機制


          在開發(fā)過程中,經(jīng)常會出現(xiàn)一個方法調(diào)用另外一個方法,那么這里就涉及到了多種場景,比如a()調(diào)用b():
          • a()和b()方法中的所有sql需要在同一個事務(wù)中嗎?
          • a()和b()方法需要單獨的事務(wù)嗎?
          • a()需要在事務(wù)中執(zhí)行,b()還需要在事務(wù)中執(zhí)行嗎?
          • 等等情況…
          所以,這就要求Spring事務(wù)能支持上面各種場景,這就是Spring事務(wù)傳播機制的由來。那Spring事務(wù)傳播機制是如何實現(xiàn)的呢?
          先來看上述幾種場景中的一種情況,a()在一個事務(wù)中執(zhí)行,調(diào)用b()方法時需要新開一個事務(wù)執(zhí)行:
          • 首先,代理對象執(zhí)行a()方法前,先利用事務(wù)管理器新建一個數(shù)據(jù)庫連接a
          • 將數(shù)據(jù)庫連接a的autocommit改為false
          • 把數(shù)據(jù)庫連接a設(shè)置到ThreadLocal中
          • 執(zhí)行a()方法中的sql
          • 執(zhí)行a()方法過程中,調(diào)用了b()方法(注意用代理對象調(diào)用b()方法)
            • 代理對象執(zhí)行b()方法前,判斷出來了當(dāng)前線程中已經(jīng)存在一個數(shù)據(jù)庫連接a了,表示當(dāng)前線程其實已經(jīng)擁有一個Spring事務(wù)了,則進行掛起
            • 掛起就是把ThreadLocal中的數(shù)據(jù)庫連接a從ThreadLocal中移除,并放入一個掛起資源對象中
            • 掛起完成后,再次利用事務(wù)管理器新建一個數(shù)據(jù)庫連接b
            • 將數(shù)據(jù)庫連接b的autocommit改為false
            • 把數(shù)據(jù)庫連接b設(shè)置到ThreadLocal中
            • 執(zhí)行b()方法中的sql
            • b()方法正常執(zhí)行完,則從ThreadLocal中拿到數(shù)據(jù)庫連接b進行提交
            • 提交之后會恢復(fù)所掛起的數(shù)據(jù)庫連接a,這里的恢復(fù),其實只是把在掛起資源對象中所保存的數(shù)據(jù)庫連接a再次設(shè)置到ThreadLocal中
          • a()方法正常執(zhí)行完,則從ThreadLocal中拿到數(shù)據(jù)庫連接a進行提交
          這個過程中最為核心的是:在執(zhí)行某個方法時,判斷當(dāng)前是否已經(jīng)存在一個事務(wù),就是判斷當(dāng)前線程的ThreadLocal中是否存在一個數(shù)據(jù)庫連接對象,如果存在則表示已經(jīng)存在一個事務(wù)了。


          五、Spring事務(wù)傳播機制分類


          其中,以非事務(wù)方式運行,表示以非Spring事務(wù)運行,表示在執(zhí)行這個方法時,Spring事務(wù)管理器不會去建立數(shù)據(jù)庫連接,執(zhí)行sql時,由Mybatis或JdbcTemplate自己來建立數(shù)據(jù)庫連接來執(zhí)行sql。
          案例分析

          情況1

          @Componentpublic class UserService { @Autowired private UserService userService;
          @Transactional public void test() { // test方法中的sql userService.a(); }
          @Transactional public void a() { // a方法中的sql }}
          默認情況下傳播機制為REQUIRED,表示當(dāng)前如果沒有事務(wù)則新建一個事務(wù),如果有事務(wù)則在當(dāng)前事務(wù)中執(zhí)行。
          所以上面這種情況的執(zhí)行流程如下:
          1. 新建一個數(shù)據(jù)庫連接conn
          2. 設(shè)置conn的autocommit為false
          3. 執(zhí)行test方法中的sql
          4. 執(zhí)行a方法中的sql
          5. 執(zhí)行conn的commit()方法進行提交
          情況2

          假如是這種情況:

          @Componentpublic class UserService { @Autowired private UserService userService;
          @Transactional public void test() { // test方法中的sql userService.a(); int result = 100/0; }
          @Transactional public void a() { // a方法中的sql }}

          所以上面這種情況的執(zhí)行流程如下:

          1. 新建一個數(shù)據(jù)庫連接conn
          2. 設(shè)置conn的autocommit為false
          3. 執(zhí)行test方法中的sql
          4. 執(zhí)行a方法中的sql
          5. 拋出異常
          6. 執(zhí)行conn的rollback()方法進行回滾,所以兩個方法中的sql都會回滾掉

          情況3

          假如是這種情況:

          @Componentpublic class UserService { @Autowired private UserService userService;
          @Transactional public void test() { // test方法中的sql userService.a(); }
          @Transactional public void a() { // a方法中的sql int result = 100/0; }}
          所以上面這種情況的執(zhí)行流程如下:
          1. 新建一個數(shù)據(jù)庫連接conn
          2. 設(shè)置conn的autocommit為false
          3. 執(zhí)行test方法中的sql
          4. 執(zhí)行a方法中的sql
          5. 拋出異常
          6. 執(zhí)行conn的rollback()方法進行回滾,所以兩個方法中的sql都會回滾掉

          情況4

          如果是這種情況:

          @Componentpublic class UserService { @Autowired private UserService userService;
          @Transactional public void test() { // test方法中的sql userService.a(); }
          @Transactional(propagation = Propagation.REQUIRES_NEW) public void a() { // a方法中的sql int result = 100/0; }}

          所以上面這種情況的執(zhí)行流程如下:

          1. 新建一個數(shù)據(jù)庫連接conn
          2. 設(shè)置conn的autocommit為false
          3. 執(zhí)行test方法中的sql
          4. 又新建一個數(shù)據(jù)庫連接conn2
          5. 執(zhí)行a方法中的sql
          6. 拋出異常
          7. 執(zhí)行conn2的rollback()方法進行回滾
          8. 繼續(xù)拋異常,對于test()方法而言,它會接收到一個異常,然后拋出
          9. 執(zhí)行conn的rollback()方法進行回滾,最終還是兩個方法中的sql都回滾了


          六、Spring事務(wù)強制回滾


          正常情況下,a()調(diào)用b()方法時,如果b()方法拋了異常,但是在a()方法捕獲了,那么a()的事務(wù)還是會正常提交的,但是有的時候,我們捕獲異常可能僅僅只是不把異常信息返回給客戶端,而是為了返回一些更友好的錯誤信息,而這個時候,我們還是希望事務(wù)能回滾的,那這個時候就得告訴Spring把當(dāng)前事務(wù)回滾掉,做法就是:
          @Transactionalpublic void test(){
          // 執(zhí)行sql try { b(); } catch (Exception e) { // 構(gòu)造友好的錯誤信息返回 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }
          }
          public void b() throws Exception { throw new Exception();}


          七、TransactionSynchronization


          Spring事務(wù)有可能會提交,回滾、掛起、恢復(fù),所以Spring事務(wù)提供了一種機制,可以讓程序員來監(jiān)聽當(dāng)前Spring事務(wù)所處于的狀態(tài)。
          @Componentpublic class UserService {
          @Autowired private JdbcTemplate jdbcTemplate;
          @Autowired private UserService userService;
          @Transactional public void test(){ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
          @Override public void suspend() { System.out.println("test被掛起了"); }
          @Override public void resume() { System.out.println("test被恢復(fù)了"); }
          @Override public void beforeCommit(boolean readOnly) { System.out.println("test準(zhǔn)備要提交了"); }
          @Override public void beforeCompletion() { System.out.println("test準(zhǔn)備要提交或回滾了"); }
          @Override public void afterCommit() { System.out.println("test提交成功了"); }
          @Override public void afterCompletion(int status) { System.out.println("test提交或回滾成功了"); } });
          jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')"); System.out.println("test"); userService.a(); }
          @Transactional(propagation = Propagation.REQUIRES_NEW) public void a(){ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
          @Override public void suspend() { System.out.println("a被掛起了"); }
          @Override public void resume() { System.out.println("a被恢復(fù)了"); }
          @Override public void beforeCommit(boolean readOnly) { System.out.println("a準(zhǔn)備要提交了"); }
          @Override public void beforeCompletion() { System.out.println("a準(zhǔn)備要提交或回滾了"); }
          @Override public void afterCommit() { System.out.println("a提交成功了"); }
          @Override public void afterCompletion(int status) { System.out.println("a提交或回滾成功了"); } });
          jdbcTemplate.execute("insert into t1 values(2,2,2,2,'2')"); System.out.println("a"); }
          }

          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構(gòu)師(附各大廠中臺建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺實踐】華為大數(shù)據(jù)中臺架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細280頁Docker實戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)


          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  你懂的视频 | 国产高清卡一卡二 | 精品一区二区三区四区五区六区七区八区九区 | 91亚洲精品久久久久久久久久久久 | 欧美性受XXXXX黑人性爽 强欧美小嫩苞第一次免费视频 |