您好,登錄后才能下訂單哦!
小編給大家分享一下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é)的例子
之前朋友問(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打臉)
看了一下文檔,原來(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)回滾!
第二個(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ō)明一下:
顯然地,我們拿到的是代理(Proxy)對(duì)象,調(diào)用addEmployee2Controller()
方法,而addEmployee2Controller()
方法的邏輯是target.addEmployee()
,調(diào)用回原始對(duì)象(target)的addEmployee()
。所以這次的調(diào)用壓根就沒(méi)有事務(wù)存在,更談不上說(shuō)Spring事務(wù)傳播機(jī)制了。
原有的數(shù)據(jù):
測(cè)試結(jié)果:壓根就沒(méi)有事務(wù)的存在
從上面的測(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é)果:
因?yàn)槲覀冇玫氖谴韺?duì)象(Proxy)去調(diào)用addEmployee()
方法,那就當(dāng)然有事務(wù)了。
看完這兩個(gè)例子,有沒(méi)有覺(jué)得3y的直覺(jué)是真的水!
如果嵌套調(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ī)制的范疇”。
我們使用的框架可能是
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工具類?
沒(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中看一下:
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)楸容^重要,所以還是在這里貼一下吧:
ResouceLoader加載配置信息
BeanDefintionReader解析配置信息,生成一個(gè)一個(gè)的BeanDefintion
BeanDefintion由BeanDefintionRegistry管理起來(lái)
BeanFactoryPostProcessor對(duì)配置信息進(jìn)行加工(也就是處理配置的信息,一般通過(guò)PropertyPlaceholderConfigurer來(lái)實(shí)現(xiàn))
實(shí)例化Bean
如果該Bean配置/實(shí)現(xiàn)
了InstantiationAwareBean,則調(diào)用對(duì)應(yīng)的方法
使用BeanWarpper來(lái)完成對(duì)象之間的屬性配置(依賴)
如果該Bean配置/實(shí)現(xiàn)了
Aware接口,則調(diào)用對(duì)應(yīng)的方法
如果該Bean配置了BeanPostProcessor的before方法,則調(diào)用
如果該Bean配置了init-method
或者實(shí)現(xiàn)InstantiationBean,則調(diào)用對(duì)應(yīng)的方法
如果該Bean配置了BeanPostProcessor的after方法,則調(diào)用
將對(duì)象放入到HashMap中
最后如果配置了destroy或者DisposableBean的方法,則執(zhí)行銷毀操作
其中也有關(guān)于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)行“加工處理”??!
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)
在聲明式事務(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è)資訊頻道!
免責(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)容。