<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    淺談 Spring 事務底層原理

    VSole2022-11-30 09:37:53

    一、@EnableTransactionManagement工作原理

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

    • AutoProxyRegistrar
    • ProxyTransactionManagementConfiguration

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

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

    • BeanFactoryTransactionAttributeSourceAdvisor:一個Advisor
    • AnnotationTransactionAttributeSource:相當于BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut
    • TransactionInterceptor:相當于BeanFactoryTransactionAttributeSourceAdvisor中的Advice

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

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

    二、Spring事務基本執行原理

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

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

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

    三、Spring事務詳細執行流程

    Spring事務執行流程圖:https://www.processon.com/view/link/5fab6edf1e0853569633cc06

    四、Spring事務傳播機制

    在開發過程中,經常會出現一個方法調用另外一個方法,那么這里就涉及到了多種場景,比如a()調用b():

    • a()和b()方法中的所有sql需要在同一個事務中嗎?
    • a()和b()方法需要單獨的事務嗎?
    • a()需要在事務中執行,b()還需要在事務中執行嗎?
    • 等等情況…

    所以,這就要求Spring事務能支持上面各種場景,這就是Spring事務傳播機制的由來。那Spring事務傳播機制是如何實現的呢?

    先來看上述幾種場景中的一種情況,a()在一個事務中執行,調用b()方法時需要新開一個事務執行:

    • 首先,代理對象執行a()方法前,先利用事務管理器新建一個數據庫連接a
    • 將數據庫連接a的autocommit改為false
    • 把數據庫連接a設置到ThreadLocal中
    • 執行a()方法中的sql
    • 執行a()方法過程中,調用了b()方法(注意用代理對象調用b()方法)
    • 代理對象執行b()方法前,判斷出來了當前線程中已經存在一個數據庫連接a了,表示當前線程其實已經擁有一個Spring事務了,則進行掛起
    • 掛起就是把ThreadLocal中的數據庫連接a從ThreadLocal中移除,并放入一個掛起資源對象中
    • 掛起完成后,再次利用事務管理器新建一個數據庫連接b
    • 將數據庫連接b的autocommit改為false
    • 把數據庫連接b設置到ThreadLocal中
    • 執行b()方法中的sql
    • b()方法正常執行完,則從ThreadLocal中拿到數據庫連接b進行提交
    • 提交之后會恢復所掛起的數據庫連接a,這里的恢復,其實只是把在掛起資源對象中所保存的數據庫連接a再次設置到ThreadLocal中
    • a()方法正常執行完,則從ThreadLocal中拿到數據庫連接a進行提交

    這個過程中最為核心的是:在執行某個方法時,判斷當前是否已經存在一個事務,就是判斷當前線程的ThreadLocal中是否存在一個數據庫連接對象,如果存在則表示已經存在一個事務了。

    五、Spring事務傳播機制分類

    其中,以非事務方式運行,表示以非Spring事務運行,表示在執行這個方法時,Spring事務管理器不會去建立數據庫連接,執行sql時,由Mybatis或JdbcTemplate自己來建立數據庫連接來執行sql。

    案例分析

    情況1

    @Component
    public class UserService {
     @Autowired
     private UserService userService;
     @Transactional
     public void test() {
      // test方法中的sql
      userService.a();
     }
     @Transactional
     public void a() {
      // a方法中的sql
     }
    }
    

    默認情況下傳播機制為REQUIRED,表示當前如果沒有事務則新建一個事務,如果有事務則在當前事務中執行。

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

    1. 新建一個數據庫連接conn
    2. 設置conn的autocommit為false
    3. 執行test方法中的sql
    4. 執行a方法中的sql
    5. 執行conn的commit()方法進行提交

    情況2

    假如是這種情況:

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

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

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

    情況3

    假如是這種情況:

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

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

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

    情況4

    如果是這種情況:

    @Component
    public 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;
     }
    }
    

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

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

    六、Spring事務強制回滾

    正常情況下,a()調用b()方法時,如果b()方法拋了異常,但是在a()方法捕獲了,那么a()的事務還是會正常提交的,但是有的時候,我們捕獲異常可能僅僅只是不把異常信息返回給客戶端,而是為了返回一些更友好的錯誤信息,而這個時候,我們還是希望事務能回滾的,那這個時候就得告訴Spring把當前事務回滾掉,做法就是:

    @Transactional
    public void test(){
     
        // 執行sql
     try {
      b();
     } catch (Exception e) {
      // 構造友好的錯誤信息返回
      TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
     }
        
    }
    public void b() throws Exception {
     throw new Exception();
    }
    

    七、TransactionSynchronization

    Spring事務有可能會提交,回滾、掛起、恢復,所以Spring事務提供了一種機制,可以讓程序員來監聽當前Spring事務所處于的狀態。

    @Component
    public 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被恢復了");
       }
       @Override
       public void beforeCommit(boolean readOnly) {
        System.out.println("test準備要提交了");
       }
       @Override
       public void beforeCompletion() {
        System.out.println("test準備要提交或回滾了");
       }
       @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被恢復了");
       }
       @Override
       public void beforeCommit(boolean readOnly) {
        System.out.println("a準備要提交了");
       }
       @Override
       public void beforeCompletion() {
        System.out.println("a準備要提交或回滾了");
       }
       @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");
     }
    }
    
    testbean
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    以下兩個工具方式的差別是,前者在獲取失敗時拋出異常。然后在代碼中就可以獲取spring容器bean了。
    部分getshell漏洞匯總
    2022-07-20 10:12:45
    即可未授權訪問console后臺,但是權限比較低備注:此處會出現個問題,在復現的環境中直接拼接
    Spring框架是一個開放源代碼的J2EE應用程序框架,是針對bean的生命周期進行管理的輕量級容器。Spring可以單獨應用于構筑應用程序,也可以和Struts、Webwork、Tapestry等眾多Web框架組合使用,并且可以與 Swing等桌面應用程序AP組合。 Spring框架主要由七部分組成,分別是 Spring Core、 Spring AOP、 Spring ORM、 Spring
    所以,這就要求Spring事務能支持上面各種場景,這就是Spring事務傳播機制的由來。
    介紹實戰中由于各種情況,可能會對反序列化Payload的長度有所限制,因此研究反序列化Payload縮小技術是有意義且必要的本文以CommonsBeanutils1鏈為示例,
    雖說是 Spring 框架漏洞,但以下包含并不僅 Spring Framework,Spring Boot,還有 Spring Cloud,Spring Data,Spring Security 等。
    雖說是 Spring 框架漏洞,但以下包含并不僅 Spring Framework,Spring Boot,還有 Spring Cloud,Spring Data,Spring Security 等。 CVE-2010-1622 Spring Framework class.classLoader 類遠程代碼執行 影響版本:SpringSource Spring Framework 3.0.0
    S2-009是S2-003與S2-005的補丁繞過,當時的補丁是增加了正則以及相關的限制,主要的防御還是正則。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类