溫馨提示×

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

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

Spring事務(wù)是什么

發(fā)布時(shí)間:2020-10-26 11:22:54 來(lái)源:億速云 閱讀:170 作者:小新 欄目:編程語(yǔ)言

小編給大家分享一下Spring事務(wù)是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

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

這里我拋出幾個(gè)問(wèn)題,看大家能不能瞬間答得上:

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

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

  • 人家所說(shuō)的BPP又是啥東西?

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

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

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

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

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

二、兩個(gè)不靠譜直覺(jué)的例子

2.1第一個(gè)例子

之前朋友問(wèn)了我一個(gè)例子:

在Service層拋出Exception,在Controller層捕獲,那如果在Service中有異常,那會(huì)事務(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):不會(huì)回滾吧。

  • 我當(dāng)時(shí)是這樣想的:因?yàn)镾ervice層已經(jīng)拋出了異常,由Controller捕獲。那是否回滾應(yīng)該由Controller的catch代碼塊中邏輯來(lái)決定,如果catch代碼塊沒(méi)有回滾,那應(yīng)該是不會(huì)回滾。

但朋友經(jīng)過(guò)測(cè)試說(shuō),可以回滾阿。(pappapa打臉)

Spring事務(wù)是什么

看了一下文檔,原來(lái)文檔有說(shuō)明:

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é)論:如果是編譯時(shí)異常不會(huì)自動(dòng)回滾,如果是運(yùn)行時(shí)異常,那會(huì)自動(dòng)回滾!

2.2第二個(gè)例子

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

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

用代碼來(lái)描述一下:

// 沒(méi)有事務(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;
}

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

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

  • Spring事務(wù)管理用的是AOP,AOP底層用的是動(dòng)態(tài)代理。所以如果我們?cè)陬惢蛘叻椒ㄉ蠘?biāo)注注解@Transactional,那么會(huì)生成一個(gè)代理對(duì)象。

接下來(lái)我用圖來(lái)說(shuō)明一下:

Spring事務(wù)是什么

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

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

Spring事務(wù)是什么

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

Spring事務(wù)是什么

2.2.1再延伸一下

從上面的測(cè)試我們可以發(fā)現(xiàn):如果是在本類中沒(méi)有事務(wù)的方法來(lái)調(diào)用標(biāo)注注解@Transactional方法,最后的結(jié)論是沒(méi)有事務(wù)的。那如果我將這個(gè)標(biāo)注注解的方法移到別的Service對(duì)象上,有沒(méi)有事務(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;
    // 沒(méi)有事務(wù)的方法去調(diào)用別的類有事務(wù)的方法
    public Employee addEmployee2Controller() throws Exception {
        return testService.addEmployee();
    }
}

測(cè)試結(jié)果:

Spring事務(wù)是什么

因?yàn)槲覀冇玫氖谴韺?duì)象(Proxy)去調(diào)用addEmployee()方法,那就當(dāng)然有事務(wù)了。

看完這兩個(gè)例子,有沒(méi)有覺(jué)得3y的直覺(jué)是真的水!

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

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

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

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

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

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

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

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

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

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

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

四、多線程問(wèn)題

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

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

Spring事務(wù)是什么

沒(méi)錯(cuò),用的就是ThreadLocal,同樣地,Spring也是用的ThreadLocal。

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

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

我們可以試著點(diǎn)一下進(jìn)去TransactionSynchronizationManager中看一下:

Spring事務(wù)是什么

五、啥是BPP?

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

  • 簡(jiǎn)單來(lái)說(shuō),通過(guò)BeanPostProcessor可以對(duì)我們的對(duì)象進(jìn)行“加工處理”。

Spring管理Bean(或者說(shuō)Bean的生命周期)也是一個(gè)???/strong>的知識(shí)點(diǎn),我在秋招也重新整理了一下步驟,因?yàn)楸容^重要,所以還是在這里貼一下吧:

  1. ResouceLoader加載配置信息

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

  3. BeanDefintion由BeanDefintionRegistry管理起來(lái)

  4. BeanFactoryPostProcessor對(duì)配置信息進(jìn)行加工(也就是處理配置的信息,一般通過(guò)PropertyPlaceholderConfigurer來(lái)實(shí)現(xiàn))

  5. 實(shí)例化Bean

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

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

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

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

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

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

  12. 將對(duì)象放入到HashMap中

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

Spring事務(wù)是什么

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

Spring事務(wù)是什么

5.1為什么特意講BPP?

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

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

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

六、認(rèn)識(shí)Spring事務(wù)幾個(gè)重要的接口

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

  • 編程式事務(wù)(通過(guò)代碼的方式來(lái)實(shí)現(xiàn)事務(wù))

  • 聲明式事務(wù)(通過(guò)配置的方式來(lái)實(shí)現(xiàn)事務(wù))

編程式事務(wù)在Spring實(shí)現(xiàn)相對(duì)簡(jiǎn)單一些,而聲明式事務(wù)因?yàn)榉庋b了大量的東西(一般我們使用簡(jiǎn)單,里頭都非常復(fù)雜),所以聲明式事務(wù)實(shí)現(xiàn)要難得多。

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

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

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

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

Spring事務(wù)是什么

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

  • TransactionProxyFactoryBean:生成代理對(duì)象

  • TransactionInterceptor:實(shí)現(xiàn)對(duì)象的攔截

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

以上是Spring事務(wù)是什么的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

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

AI