只有光頭才能變強。java
文本已收錄至個人GitHub倉庫,歡迎Star:https://github.com/ZhongFuCheng3y/3ygit
Spring事務管理我相信你們都用得不少,但可能僅僅侷限於一個@Transactional
註解或者在XML
中配置事務相關的東西。無論怎麼說,平常可能足夠咱們去用了。但做爲程序員,不管是爲了面試仍是說更好把控本身寫的代碼,仍是應該得多多瞭解一下Spring事務的一些細節。程序員
這裏我拋出幾個問題,看你們能不能瞬間答得上:github
Hibernate/JPA
或者是Mybatis
,都知道的底層是須要一個session/connection
對象來幫咱們執行操做的。要保證事務的完整性,咱們須要多組數據庫操做要使用同一個session/connection
對象,而咱們又知道Spring IOC所管理的對象默認都是單例的,這爲啥咱們在使用的時候不會引起線程安全問題呢?內部Spring到底幹了什麼?閱讀這篇文章的同窗我默認你們都對Spring事務相關知識有必定的瞭解了。(ps:若是不瞭解點解具體的文章去閱讀再回到這裏來哦)面試
咱們都知道,Spring事務是Spring AOP的最佳實踐之一,因此說AOP入門基礎知識(簡單配置,使用)是須要先知道的。若是想更加全面瞭解AOP能夠看這篇文章:AOP重要知識點(術語介紹、全面使用)。說到AOP就不能不說AOP底層原理:動態代理設計模式。到這裏,對AOP已經有一個基礎的認識了。因而咱們就能夠使用XML/註解方式來配置Spring事務管理。spring
在IOC學習中,能夠知道的是Spring中Bean的生命週期(引出BPP對象)而且IOC所管理的對象默認都是單例的:單例設計模式,單例對象若是有"狀態"(有成員變量),那麼多線程訪問這個單例對象,可能就形成線程不安全。那麼何爲線程安全?,解決線程安全有不少方式,但其中有一種:讓每個線程都擁有本身的一個變量:ThreadLocal數據庫
若是對我以上說的知識點不太瞭解的話,建議點擊藍字進去學習一番。編程
以前朋友問了我一個例子:設計模式
在Service層拋出Exception,在Controller層捕獲,那若是在Service中有異常,那會事務回滾嗎?安全
// Service方法 @Transactional public Employee addEmployee() throws Exception { Employee employee = new Employee("3y", 23); employeeRepository.save(employee); // 假設這裏出了Exception int i = 1 / 0; return employee; } // Controller調用 @RequestMapping("/add") public Employee addEmployee() { Employee employee = null; try { employee = employeeService.addEmployee(); } catch (Exception e) { e.printStackTrace(); } return employee; }
我第一反應:不會回滾吧。
但朋友通過測試說,能夠回滾阿。(pappapa打臉)
看了一下文檔,原來文檔有說明:
By default checked exceptions do not result in the transactional interceptor marking the transaction for rollback and instances of RuntimeException and its subclasses do
結論:若是是編譯時異常不會自動回滾,若是是運行時異常,那會自動回滾!
第二個例子來源於知乎@柳樹文章,文末會給出相應的URL
咱們都知道,帶有@Transactional
註解所包圍的方法就能被Spring事務管理起來,那若是我在當前類下使用一個沒有事務的方法去調用一個有事務的方法,那咱們此次調用會怎麼樣?是否會有事務呢?
用代碼來描述一下:
// 沒有事務的方法去調用有事務的方法 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事務的傳播機制有關吧。
其實這跟Spring事務的傳播機制沒有關係,下面我講述一下:
@Transactional
,那麼會生成一個代理對象。接下來我用圖來講明一下:
顯然地,咱們拿到的是代理(Proxy)對象,調用addEmployee2Controller()
方法,而addEmployee2Controller()
方法的邏輯是target.addEmployee()
,調用回原始對象(target)的addEmployee()
。因此此次的調用壓根就沒有事務存在,更談不上說Spring事務傳播機制了。
原有的數據:
測試結果:壓根就沒有事務的存在
從上面的測試咱們能夠發現:若是是在本類中沒有事務的方法來調用標註註解@Transactional
方法,最後的結論是沒有事務的。那若是我將這個標註註解的方法移到別的Service對象上,有沒有事務?
@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; // 沒有事務的方法去調用別的類有事務的方法 public Employee addEmployee2Controller() throws Exception { return testService.addEmployee(); } }
測試結果:
由於咱們用的是代理對象(Proxy)去調用addEmployee()
方法,那就固然有事務了。
看完這兩個例子,有沒有以爲3y的直覺是真的水!
若是嵌套調用含有事務的方法,在Spring事務管理中,這屬於哪一個知識點?
在當前含有事務方法內部調用其餘的方法(不管該方法是否含有事務),這就屬於Spring事務傳播機制的知識點範疇了。
Spring事務基於Spring AOP,Spring AOP底層用的動態代理,動態代理有兩種方式:
至於爲啥以上的狀況不能加強,用大家的腦瓜子想一下就知道了。
值得說明的是:那些不能被Spring AOP加強的方法並非不能在事務環境下工做了。只要它們被外層的事務方法調用了,因爲Spring事務管理的傳播級別,內部方法也能夠工做在外部方法所啓動的事務上下文中。
至於Spring事務傳播機制的幾個級別,我在這裏就不貼出來了。這裏只是再次解釋「啥狀況纔是屬於Spring事務傳播機制的範疇」。
咱們使用的框架多是
Hibernate/JPA
或者是Mybatis
,都知道的底層是須要一個session/connection
對象來幫咱們執行操做的。要保證事務的完整性,咱們須要多組數據庫操做要使用同一個session/connection
對象,而咱們又知道Spring IOC所管理的對象默認都是單例的,這爲啥咱們在使用的時候不會引起線程安全問題呢?內部Spring到底幹了什麼?
回想一下當年咱們學Mybaits的時候,是怎麼編寫Session工具類?
沒錯,用的就是ThreadLocal,一樣地,Spring也是用的ThreadLocal。
如下內容來源《精通 Spring4.x》
咱們知道在通常狀況下,只有無狀態的Bean才能夠在多線程環境下共享,在Spring中,絕大部分Bean均可以聲明爲singleton做用域。就是由於Spring對一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態的「狀態性對象」採用ThreadLocal封裝,讓它們也成爲線程安全的「狀態性對象」,所以,有狀態的Bean就可以以singleton的方式在多線程中工做。
咱們能夠試着點一下進去TransactionSynchronizationManager中看一下:
BBP的全稱叫作:BeanPostProcessor,通常咱們俗稱對象後處理器
Spring管理Bean(或者說Bean的生命週期)也是一個常考的知識點,我在秋招也從新整理了一下步驟,由於比較重要,因此仍是在這裏貼一下吧:
配置/實現
了InstantiationAwareBean,則調用對應的方法配置/實現了
Aware接口,則調用對應的方法init-method
或者實現InstantiationBean,則調用對應的方法其中也有關於BPP圖片:
Spring AOP編程底層經過的是動態代理技術,在調用的時候確定用的是代理對象。那麼Spring是怎麼作的呢?
我只須要寫一個BPP,在postProcessBeforeInitialization或者postProcessAfterInitialization方法中,對對象進行判斷,看他需不須要織入切面邏輯,若是須要,那我就根據這個對象,生成一個代理對象,而後返回這個代理對象,那麼最終注入容器的,天然就是代理對象了。
Spring提供了BeanPostProcessor,就是讓咱們能夠對有須要的對象進行「加工處理」啊!
Spring事務能夠分爲兩種:
編程式事務在Spring實現相對簡單一些,而聲明式事務由於封裝了大量的東西(通常咱們使用簡單,裏頭都很是複雜),因此聲明式事務實現要可貴多。
在編程式事務中有如下幾個重要的了接口:
在聲明式事務中,除了TransactionStatus和PlatformTransactionManager接口,還有幾個重要的接口:
本文主要講了Spring事務管理一些比較重要的知識點,固然在學習的過程當中還看到其餘的知識點,若是想要繼續學習的同窗不妨經過下面給出的參考資料繼續閱讀。
參考資料:
樂於輸出乾貨的Java技術公衆號:Java3y。公衆號內有200多篇原創技術文章、海量視頻資源、精美腦圖,不妨來關注一下!
以爲個人文章寫得不錯,不妨點一下贊!