溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring事務(wù)的詳細介紹

發(fā)布時間:2021-08-31 16:43:47 來源:億速云 閱讀:121 作者:chen 欄目:編程語言

本篇內(nèi)容介紹了“Spring事務(wù)的詳細介紹”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

前言

Spring事務(wù)管理我相信大家都用得很多,但可能僅僅局限于一個@Transactional注解或者在XML中配置事務(wù)相關(guān)的東西。不管怎么說,日常可能足夠我們?nèi)ビ昧恕5鳛槌绦騿T,無論是為了面試還是說更好把控自己寫的代碼,還是應(yīng)該得多多了解一下Spring事務(wù)的一些細節(jié)。

這里我拋出幾個問題,看大家能不能瞬間答得上:

  • 如果嵌套調(diào)用含有事務(wù)的方法,在Spring事務(wù)管理中,這屬于哪個知識點?

  • 我們使用的框架可能是Hibernate/JPA或者是Mybatis,都知道的底層是需要一個session/connection對象來幫我們執(zhí)行操作的。要保證事務(wù)的完整性,我們需要多組數(shù)據(jù)庫操作要使用同一個session/connection對象,而我們又知道Spring IOC所管理的對象默認都是單例的,這為啥我們在使用的時候不會引發(fā)線程安全問題呢?內(nèi)部Spring到底干了什么?

  • 人家所說的BPP又是啥東西?

  • Spring事務(wù)管理重要接口有哪幾個?

一、閱讀本文需要的基礎(chǔ)知識

閱讀這篇文章的同學(xué)我默認大家都對Spring事務(wù)相關(guān)知識有一定的了解了。(ps:如果不了解點解具體的文章去閱讀再回到這里來哦)

我們都知道,Spring事務(wù)是Spring AOP的最佳實踐之一,所以說AOP入門基礎(chǔ)知識(簡單配置,使用)是需要先知道的。如果想更加全面了解AOP可以看這篇文章:AOP重要知識點(術(shù)語介紹、全面使用)。說到AOP就不能不說AOP底層原理:動態(tài)代理設(shè)計模式。到這里,對AOP已經(jīng)有一個基礎(chǔ)的認識了。于是我們就可以使用XML/注解方式來配置Spring事務(wù)管理。

在IOC學(xué)習(xí)中,可以知道的是Spring中Bean的生命周期(引出BPP對象)并且IOC所管理的對象默認都是單例的:單例設(shè)計模式,單例對象如果有"狀態(tài)"(有成員變量),那么多線程訪問這個單例對象,可能就造成線程不安全。那么何為線程安全?,解決線程安全有很多方式,但其中有一種:讓每一個線程都擁有自己的一個變量:ThreadLocal

如果對我以上說的知識點不太了解的話,建議點擊藍字進去學(xué)習(xí)一番。

二、兩個不靠譜直覺的例子

2.1第一個例子

之前朋友問了我一個例子:

在Service層拋出Exception,在Controller層捕獲,那如果在Service中有異常,那會事務(wù)回滾嗎?

// Service方法

@Transactional
public Employee addEmployee() throws Exception {

    Employee employee = new Employee("3y", 23);
    employeeRepository.save(employee);
    // 假設(shè)這里出了Exception
    int i = 1 / 0;

    return employee;
}

// Controller調(diào)用
@RequestMapping("/add")
public Employee addEmployee() {
    Employee employee = null;
    try {
        employee = employeeService.addEmployee();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return employee;

}

第一反應(yīng):不會回滾吧。

  • 我當(dāng)時是這樣想的:因為Service層已經(jīng)拋出了異常,由Controller捕獲。那是否回滾應(yīng)該由Controller的catch代碼塊中邏輯來決定,如果catch代碼塊沒有回滾,那應(yīng)該是不會回滾。

但朋友經(jīng)過測試說,可以回滾阿。(pappapa打臉)

Spring事務(wù)的詳細介紹

看了一下文檔,原來文檔有說明:

By default checked exceptions do not result in the transactional interceptor marking the transaction for rollback and instances of RuntimeException and its  subclasses do

結(jié)論:如果是編譯時異常不會自動回滾,如果是運行時異常,那會自動回滾!

2.2第二個例子

第二個例子來源于知乎@柳樹文章,文末會給出相應(yīng)的URL

我們都知道,帶有@Transactional注解所包圍的方法就能被Spring事務(wù)管理起來,那如果我在當(dāng)前類下使用一個沒有事務(wù)的方法去調(diào)用一個有事務(wù)的方法,那我們這次調(diào)用會怎么樣?是否會有事務(wù)呢?

用代碼來描述一下:

// 沒有事務(wù)的方法去調(diào)用有事務(wù)的方法
public Employee addEmployee2Controller() throws Exception {

    return this.addEmployee();
}

@Transactional
public Employee addEmployee() throws Exception {

    employeeRepository.deleteAll();
    Employee employee = new Employee("3y", 23);

    // 模擬異常
    int i = 1 / 0;

    return employee;
}

我第一直覺是:這跟Spring事務(wù)的傳播機制有關(guān)吧。

其實這跟Spring事務(wù)的傳播機制沒有關(guān)系,下面我講述一下:

  • Spring事務(wù)管理用的是AOP,AOP底層用的是動態(tài)代理。所以如果我們在類或者方法上標(biāo)注注解@Transactional,那么會生成一個代理對象。

接下來我用圖來說明一下:

Spring事務(wù)的詳細介紹

顯然地,我們拿到的是代理(Proxy)對象,調(diào)用addEmployee2Controller()方法,而addEmployee2Controller()方法的邏輯是target.addEmployee(),調(diào)用回原始對象(target)的addEmployee()。所以這次的調(diào)用壓根就沒有事務(wù)存在,更談不上說Spring事務(wù)傳播機制了。

原有的數(shù)據(jù):

Spring事務(wù)的詳細介紹

測試結(jié)果:壓根就沒有事務(wù)的存在

Spring事務(wù)的詳細介紹
2.2.1再延伸一下

從上面的測試我們可以發(fā)現(xiàn):如果是在本類中沒有事務(wù)的方法來調(diào)用標(biāo)注注解@Transactional方法,最后的結(jié)論是沒有事務(wù)的。那如果我將這個標(biāo)注注解的方法移到別的Service對象上,有沒有事務(wù)?

@Service
public class TestService {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Transactional
    public Employee addEmployee() throws Exception {

        employeeRepository.deleteAll();

        Employee employee = new Employee("3y", 23);

        // 模擬異常
        int i = 1 / 0;

        return employee;
    }

}


@Service
public class EmployeeService {

    @Autowired
    private TestService testService;
    // 沒有事務(wù)的方法去調(diào)用別的類有事務(wù)的方法
    public Employee addEmployee2Controller() throws Exception {
        return testService.addEmployee();
    }
}

測試結(jié)果:

Spring事務(wù)的詳細介紹

因為我們用的是代理對象(Proxy)去調(diào)用addEmployee()方法,那就當(dāng)然有事務(wù)了。

看完這兩個例子,有沒有覺得3y的直覺是真的水!

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

如果嵌套調(diào)用含有事務(wù)的方法,在Spring事務(wù)管理中,這屬于哪個知識點?

在當(dāng)前含有事務(wù)方法內(nèi)部調(diào)用其他的方法(無論該方法是否含有事務(wù)),這就屬于Spring事務(wù)傳播機制的知識點范疇了。

Spring事務(wù)基于Spring AOP,Spring AOP底層用的動態(tài)代理,動態(tài)代理有兩種方式:

  • 基于接口代理(JDK代理)

    • 基于接口代理,凡是類的方法非public修飾,或者用了static關(guān)鍵字修飾,那這些方法都不能被Spring AOP增強

  • 基于CGLib代理(子類代理)

    • 基于子類代理,凡是類的方法使用了private、static、final修飾,那這些方法都不能被Spring AOP增強

至于為啥以上的情況不能增強,用你們的腦瓜子想一下就知道了。

值得說明的是:那些不能被Spring AOP增強的方法并不是不能在事務(wù)環(huán)境下工作了。只要它們被外層的事務(wù)方法調(diào)用了,由于Spring事務(wù)管理的傳播級別,內(nèi)部方法也可以工作在外部方法所啟動的事務(wù)上下文中。

至于Spring事務(wù)傳播機制的幾個級別,我在這里就不貼出來了。這里只是再次解釋“啥情況才是屬于Spring事務(wù)傳播機制的范疇”。

四、多線程問題

我們使用的框架可能是Hibernate/JPA或者是Mybatis,都知道的底層是需要一個session/connection對象來幫我們執(zhí)行操作的。要保證事務(wù)的完整性,我們需要多組數(shù)據(jù)庫操作要使用同一個session/connection對象,而我們又知道Spring IOC所管理的對象默認都是單例的,這為啥我們在使用的時候不會引發(fā)線程安全問題呢?內(nèi)部Spring到底干了什么?

回想一下當(dāng)年我們學(xué)Mybaits的時候,是怎么編寫Session工具類?

Spring事務(wù)的詳細介紹

沒錯,用的就是ThreadLocal,同樣地,Spring也是用的ThreadLocal。

以下內(nèi)容來源《精通 Spring4.x》

我們知道在一般情況下,只有無狀態(tài)的Bean才可以在多線程環(huán)境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域。就是因為Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態(tài)的“狀態(tài)性對象”采用ThreadLocal封裝,讓它們也成為線程安全的“狀態(tài)性對象”,因此,有狀態(tài)的Bean就能夠以singleton的方式在多線程中工作。

我們可以試著點一下進去TransactionSynchronizationManager中看一下:

Spring事務(wù)的詳細介紹

五、啥是BPP?

BBP的全稱叫做:BeanPostProcessor,一般我們俗稱對象后處理器

  • 簡單來說,通過BeanPostProcessor可以對我們的對象進行“加工處理”。

Spring管理Bean(或者說Bean的生命周期)也是一個???/strong>的知識點,我在秋招也重新整理了一下步驟,因為比較重要,所以還是在這里貼一下吧:

  1. ResouceLoader加載配置信息

  2. BeanDefintionReader解析配置信息,生成一個一個的BeanDefintion

  3. BeanDefintion由BeanDefintionRegistry管理起來

  4. BeanFactoryPostProcessor對配置信息進行加工(也就是處理配置的信息,一般通過PropertyPlaceholderConfigurer來實現(xiàn))

  5. 實例化Bean

  6. 如果該Bean配置/實現(xiàn)了InstantiationAwareBean,則調(diào)用對應(yīng)的方法

  7. 使用BeanWarpper來完成對象之間的屬性配置(依賴)

  8. 如果該Bean配置/實現(xiàn)了Aware接口,則調(diào)用對應(yīng)的方法

  9. 如果該Bean配置了BeanPostProcessor的before方法,則調(diào)用

  10. 如果該Bean配置了init-method或者實現(xiàn)InstantiationBean,則調(diào)用對應(yīng)的方法

  11. 如果該Bean配置了BeanPostProcessor的after方法,則調(diào)用

  12. 將對象放入到HashMap中

  13. 最后如果配置了destroy或者DisposableBean的方法,則執(zhí)行銷毀操作

Spring事務(wù)的詳細介紹

其中也有關(guān)于BPP圖片:

Spring事務(wù)的詳細介紹

5.1為什么特意講BPP?

Spring AOP編程底層通過的是動態(tài)代理技術(shù),在調(diào)用的時候肯定用的是代理對象。那么Spring是怎么做的呢?

我只需要寫一個BPP,在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,對對象進行判斷,看他需不需要織入切面邏輯,如果需要,那我就根據(jù)這個對象,生成一個代理對象,然后返回這個代理對象,那么最終注入容器的,自然就是代理對象了。

Spring提供了BeanPostProcessor,就是讓我們可以對有需要的對象進行“加工處理”??!

六、認識Spring事務(wù)幾個重要的接口

Spring事務(wù)可以分為兩種:

  • 編程式事務(wù)(通過代碼的方式來實現(xiàn)事務(wù))

  • 聲明式事務(wù)(通過配置的方式來實現(xiàn)事務(wù))

編程式事務(wù)在Spring實現(xiàn)相對簡單一些,而聲明式事務(wù)因為封裝了大量的東西(一般我們使用簡單,里頭都非常復(fù)雜),所以聲明式事務(wù)實現(xiàn)要難得多。

在編程式事務(wù)中有以下幾個重要的了接口:

  • TransactionDefinition:定義了Spring兼容的事務(wù)屬性(比如事務(wù)隔離級別、事務(wù)傳播、事務(wù)超時、是否只讀狀態(tài))

  • TransactionStatus:代表了事務(wù)的具體運行狀態(tài)(獲取事務(wù)運行狀態(tài)的信息,也可以通過該接口間接回滾事務(wù)等操作)

  • PlatformTransactionManager:事務(wù)管理器接口(定義了一組行為,具體實現(xiàn)交由不同的持久化框架來完成---類比JDBC)

Spring事務(wù)的詳細介紹

在聲明式事務(wù)中,除了TransactionStatus和PlatformTransactionManager接口,還有幾個重要的接口:

  • TransactionProxyFactoryBean:生成代理對象

  • TransactionInterceptor:實現(xiàn)對象的攔截

  • TransactionAttrubute:事務(wù)配置的數(shù)據(jù)

“Spring事務(wù)的詳細介紹”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI