javascript
Spring事务原理一探
概括來講,事務(wù)是一個由有限操作集合組成的邏輯單元。事務(wù)操作包含兩個目的,數(shù)據(jù)一致以及操作隔離。數(shù)據(jù)一致是指事務(wù)提交時保證事務(wù)內(nèi)的所有操作都成功完成,并且更改永久生效;事務(wù)回滾時,保證能夠恢復到事務(wù)執(zhí)行之前的狀態(tài)。操作隔離則是指多個同時執(zhí)行的事務(wù)之間應(yīng)該相互獨立,互不影響。
事務(wù)是一個比較廣泛的概念,事務(wù)管理資源除了我們熟知的數(shù)據(jù)庫外,還可以包含消息隊列、文件系統(tǒng)等。當然,一般來說,我們說的事務(wù)單指“數(shù)據(jù)庫事務(wù)”。接下來我們會以MySQL數(shù)據(jù)庫、Spring聲明式事務(wù)為主要研究對象,但是很多事務(wù)概念、接口抽象和實現(xiàn)方式同時適用于其他情況。
想要技術(shù)干貨、行業(yè)洞察,歡迎關(guān)注網(wǎng)易云信博客。
了解網(wǎng)易云信,來自網(wǎng)易核心架構(gòu)的通信與視頻云服務(wù)。
事務(wù)屬性和行為
ACID屬性
提到事務(wù),不可避免需要涉及到事務(wù)的ACID屬性:
- 原子性(Atomicity):事務(wù)作為一個整體被執(zhí)行,包含在其中的對數(shù)據(jù)庫的操作要么全部被執(zhí)行,要么都不執(zhí)行。
- 一致性(Consistency):事務(wù)應(yīng)確保數(shù)據(jù)庫的狀態(tài)從一個一致狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€一致狀態(tài)。一致狀態(tài)的含義是數(shù)據(jù)庫中的數(shù)據(jù)應(yīng)滿足完整性約束。
- 隔離性(Isolation):多個事務(wù)并發(fā)執(zhí)行時,一個事務(wù)的執(zhí)行不應(yīng)影響其他事務(wù)的執(zhí)行。
- 持久性(Durability):已被提交的事務(wù)對數(shù)據(jù)庫的修改應(yīng)該永久保存在數(shù)據(jù)庫中。
我們將嚴格遵循ACID屬性的事務(wù)稱為剛性事務(wù)。與之相對,期望最終一致性,在事務(wù)執(zhí)行的中間狀態(tài)允許暫時不遵循ACID屬性的事務(wù)稱為柔性事務(wù),可參考傳統(tǒng)事務(wù)與柔性事務(wù),柔性事務(wù)的使用涉及到分布式事務(wù)方案,可以后續(xù)擴展,這里我們先將注意集中在事務(wù)實現(xiàn)原理上。
隔離級別
根據(jù)SQL92標準,MySQL的InnoDB引擎提供四種隔離級別(即ACID中的I):讀未提交(READ UNCOMMITTED)、讀已提交(READ COMMITTED)、可重復讀(REPEATABLE READ)和串行化(SERIALIZABLE),InnoDB默認的隔離級別是REPEATABLE READ,其可避免臟讀和不可重復讀,但不能避免幻讀,需要指出的是,InnoDB引擎的多版本并發(fā)控制機制(MVCC)并沒有完全避免幻讀,關(guān)于該問題以及隔離級別說明,可參考MySQL的InnoDB的幻讀問題。
傳播機制
Spring針對方法嵌套調(diào)用時事務(wù)的創(chuàng)建行為定義了七種事務(wù)傳播機制,分別是PROPAGATION_REQUIRED、PROPAGATION_SUPPORT、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER以及PROPAGATION_NESTED,基本上從字面意思就能知道每種傳播機制各自的行為表現(xiàn),Spring默認的事務(wù)傳播機制是PROPAGATION_REQUIRED,即如果當前存在事務(wù),則使用當前事務(wù),否則創(chuàng)建新的事務(wù)。詳情可參考Spring事務(wù)傳播行為。
事務(wù)行為
事務(wù)的行為包括事務(wù)開啟、事務(wù)提交和事務(wù)回滾。InnoDB所有的用戶SQL執(zhí)行都在事務(wù)控制之內(nèi),在默認情況下,autocommit設(shè)置為true,單條SQL執(zhí)行成功后,MySQL會自動提交事務(wù),或者如果SQL執(zhí)行出錯,則根據(jù)異常類型執(zhí)行事務(wù)提交或者回滾。可以使用START TRANSACTION(SQL標準)或者BEGIN開啟事務(wù),使用COMMIT和ROLLBACK提交和回滾事務(wù);也可以通過設(shè)置autocommit屬性來控制事務(wù)行為,當設(shè)置autocommit為false時,其后執(zhí)行的多條SQL語句將在一個事務(wù)內(nèi),直到執(zhí)行COMMIT或者ROLLBACK事務(wù)才會提交或者回滾。
AOP增強
Spring使用AOP(面向切面編程)來實現(xiàn)聲明式事務(wù),后續(xù)在講Spring事務(wù)具體實現(xiàn)的時候會詳細說明,關(guān)于AOP的概念可參考Spring AOP概念理解(通俗易懂),這里不再細說。說下動態(tài)代理和AOP增強。
動態(tài)代理是Spring實現(xiàn)AOP的默認方式,分為兩種:JDK動態(tài)代理和CGLIB動態(tài)代理。JDK動態(tài)代理面向接口,通過反射生成目標代理接口的匿名實現(xiàn)類;CGLIB動態(tài)代理則通過繼承,使用字節(jié)碼增強技術(shù)(或者objenesis類庫)為目標代理類生成代理子類。Spring默認對接口實現(xiàn)使用JDK動態(tài)代理,對具體類使用CGLIB,同時也支持配置全局使用CGLIB來生成代理對象。
我們在切面配置中會使用到@Aspect注解,這里用到了Aspectj的切面表達式。Aspectj是java語言實現(xiàn)的一個AOP框架,使用靜態(tài)代理模式,擁有完善的AOP功能,與Spring AOP互為補充。Spring采用了Aspectj強大的切面表達式定義方式,但是默認情況下仍然使用動態(tài)代理方式,并未使用Aspectj的編譯器和織入器,當然也支持配置使用Aspectj靜態(tài)代理替代動態(tài)代理方式。Aspectj功能更強大,比方說它支持對字段、POJO類進行增強,與之相對,Spring只支持對Bean方法級別進行增強。
Spring對方法的增強有五種方式:
- 前置增強(org.springframework.aop.BeforeAdvice):在目標方法執(zhí)行之前進行增強;
- 后置增強(org.springframework.aop.AfterReturningAdvice):在目標方法執(zhí)行之后進行增強;
- 環(huán)繞增強(org.aopalliance.intercept.MethodInterceptor):在目標方法執(zhí)行前后都執(zhí)行增強;
- 異常拋出增強(org.springframework.aop.ThrowsAdvice):在目標方法拋出異常后執(zhí)行增強;
- 引介增強(org.springframework.aop.IntroductionInterceptor):為目標類添加新的方法和屬性。
聲明式事務(wù)的實現(xiàn)就是通過環(huán)繞增強的方式,在目標方法執(zhí)行之前開啟事務(wù),在目標方法執(zhí)行之后提交或者回滾事務(wù),事務(wù)攔截器的繼承關(guān)系圖可以體現(xiàn)這一點:
?
Spring事務(wù)抽象
統(tǒng)一一致的事務(wù)抽象是Spring框架的一大優(yōu)勢,無論是全局事務(wù)還是本地事務(wù),JTA、JDBC、Hibernate還是JPA,Spring都使用統(tǒng)一的編程模型,使得應(yīng)用程序可以很容易地在全局事務(wù)與本地事務(wù),或者不同的事務(wù)框架之間進行切換。下圖是Spring事務(wù)抽象的核心類圖:
?
接口PlatformTransactionManager定義了事務(wù)操作的行為,其依賴TransactionDefinition和TransactionStatus接口,其實大部分的事務(wù)屬性和行為我們以MySQL數(shù)據(jù)庫為例已經(jīng)有過了解,這里再對應(yīng)介紹下。
- PlatformTransactionManager:事務(wù)管理器
- getTransaction方法:事務(wù)獲取操作,根據(jù)事務(wù)屬性定義,獲取當前事務(wù)或者創(chuàng)建新事物;
- commit方法:事務(wù)提交操作,注意這里所說的提交并非直接提交事務(wù),而是根據(jù)當前事務(wù)狀態(tài)執(zhí)行提交或者回滾操作;
- rollback方法:事務(wù)回滾操作,同樣,也并非一定直接回滾事務(wù),也有可能只是標記事務(wù)為只讀,等待其他調(diào)用方執(zhí)行回滾。
- TransactionDefinition:事務(wù)屬性定義
- getPropagationBehavior方法:返回事務(wù)的傳播屬性,默認是PROPAGATION_REQUIRED;
- getIsolationLevel方法:返回事務(wù)隔離級別,事務(wù)隔離級別只有在創(chuàng)建新事務(wù)時才有效,也就是說只對應(yīng)傳播屬性PROPAGATION_REQUIRED和PROPAGATION_REQUIRES_NEW;
- getTimeout方法:返回事務(wù)超時時間,以秒為單位,同樣只有在創(chuàng)建新事務(wù)時才有效;
- isReadOnly方法:是否優(yōu)化為只讀事務(wù),支持這項屬性的事務(wù)管理器會將事務(wù)標記為只讀,只讀事務(wù)不允許有寫操作,不支持只讀屬性的事務(wù)管理器需要忽略這項設(shè)置,這一點跟其他事務(wù)屬性定義不同,針對其他不支持的屬性設(shè)置,事務(wù)管理器應(yīng)該拋出異常。
- getName方法:返回事務(wù)名稱,聲明式事務(wù)中默認值為“類的完全限定名.方法名”。
- TransactionStatus:當前事務(wù)狀態(tài)
- isNewTransaction方法:當前方法是否創(chuàng)建了新事務(wù)(區(qū)別于使用現(xiàn)有事務(wù)以及沒有事務(wù));
- hasSavepoint方法:在嵌套事務(wù)場景中,判斷當前事務(wù)是否包含保存點;
- setRollbackOnly和isRollbackOnly方法:只讀屬性設(shè)置(主要用于標記事務(wù),等待回滾)和查詢;
- flush方法:刷新底層會話中的修改到數(shù)據(jù)庫,一般用于刷新如Hibernate/JPA的會話,是否生效由具體事務(wù)資源實現(xiàn)決定;
- isCompleted方法:判斷當前事務(wù)是否已完成(已提交或者已回滾)。
部分Spring包含的對PlatformTransactionManager的實現(xiàn)類如下圖所示:
?
AbstractPlatformTransactionManager抽象類實現(xiàn)了Spring事務(wù)的標準流程,其子類DataSourceTransactionManager是我們使用較多的JDBC單數(shù)據(jù)源事務(wù)管理器,而JtaTransactionManager是JTA(Java Transaction API)規(guī)范的實現(xiàn)類,另外兩個則分別是JavaEE容器WebLogic和WebSphere的JTA事務(wù)管理器的具體實現(xiàn)。
Spring事務(wù)切面
之前提到,Spring采用AOP來實現(xiàn)聲明式事務(wù),那么事務(wù)的AOP切面是如何織入的呢?這一點涉及到AOP動態(tài)代理對象的生成過程。
代理對象生成的核心類是AbstractAutoProxyCreator,實現(xiàn)了BeanPostProcessor接口,會在Bean初始化完成之后,通過postProcessAfterInitialization方法生成代理對象,關(guān)于BeanPostProcessor在Bean生命周期中的作用,可參考一些常用的Spring擴展接口。
看一下AbstractAutoProxyCreator類的核心代碼,主要關(guān)注三個方法:postProcessAfterInitialization、wrapIfNecessary和createProxy,為了突出核心流程,以注釋代替了部分代碼的具體實現(xiàn),后續(xù)的源碼分析也采用相同的處理。
// AbstractAutoProxyCreator.class @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {// 創(chuàng)建代理對象return wrapIfNecessary(bean, beanName, cacheKey);}}return bean; }protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 參數(shù)檢查,跳過已經(jīng)執(zhí)行過代理對象生成,或者已知的不需要生成代理對象的Bean...// Create proxy if we have advice.// 查詢當前Bean所有的AOP增強配置,最終是通過AOPUtils工具類實現(xiàn)Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 執(zhí)行AOP織入,創(chuàng)建代理對象Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean; }protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}// 實例化代理工廠類ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);// 當全局使用動態(tài)代理時,設(shè)置是否需要對目標Bean強制使用CGLIB動態(tài)代理...// 構(gòu)建AOP增強顧問,包含框架公共增強和應(yīng)用程序自定義增強// 設(shè)置proxyFactory屬性,如增強、目標類、是否允許變更等...// 創(chuàng)建代理對象return proxyFactory.getProxy(getProxyClassLoader()); }最后是通過調(diào)用ProxyFactory#getProxy(java.lang.ClassLoader)方法來創(chuàng)建代理對象:
// ProxyFactory.class public Object getProxy(ClassLoader classLoader) {return createAopProxy().getProxy(classLoader); }// ProxyFactory父類ProxyCreatorSupport.class protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this); }public ProxyCreatorSupport() {this.aopProxyFactory = new DefaultAopProxyFactory(); }ProxyFactory的父類構(gòu)造器實例化了DefaultAopProxyFactory類,從其源代碼我們可以看到Spring動態(tài)代理方式選擇策略的實現(xiàn):如果目標類optimize,proxyTargetClass屬性設(shè)置為true或者未指定需要代理的接口,則使用CGLIB生成代理對象,否則使用JDK動態(tài)代理。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 如果optimize,proxyTargetClass屬性設(shè)置為true或者未指定代理接口,則使用CGLIB生成代理對象if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();// 參數(shù)檢查,targetClass為空拋出異常...// 目標類本身是接口或者代理對象,仍然使用JDK動態(tài)代理if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// Objenesis是一個可以不通過構(gòu)造器創(chuàng)建子類的java工具類庫// 作為Spring 4.0后CGLIB的默認實現(xiàn)return new ObjenesisCglibAopProxy(config);}else {// 否則使用JDK動態(tài)代理return new JdkDynamicAopProxy(config);}}... }Spring事務(wù)攔截
我們已經(jīng)了解了AOP切面織入生成代理對象的過程,當Bean方法通過代理對象調(diào)用時,會觸發(fā)對應(yīng)的AOP增強攔截器,前面提到聲明式事務(wù)是一種環(huán)繞增強,對應(yīng)接口為MethodInterceptor,事務(wù)增強對該接口的實現(xiàn)為TransactionInterceptor,類圖如下:
事務(wù)攔截器TransactionInterceptor在invoke方法中,通過調(diào)用父類TransactionAspectSupport的invokeWithinTransaction方法進行事務(wù)處理,該方法支持聲明式事務(wù)和編程式事務(wù)。
// TransactionInterceptor.class @Override public Object invoke(final MethodInvocation invocation) throws Throwable {// 獲取targetClass...// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {// 實際執(zhí)行目標方法return invocation.proceed();}}); }// TransactionInterceptor父類TransactionAspectSupport.class protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {// If the transaction attribute is null, the method is non-transactional.// 查詢目標方法事務(wù)屬性、確定事務(wù)管理器、構(gòu)造連接點標識(用于確認事務(wù)名稱)final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// 事務(wù)獲取TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 通過回調(diào)執(zhí)行目標方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 目標方法執(zhí)行拋出異常,根據(jù)異常類型執(zhí)行事務(wù)提交或者回滾操作completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// 清理當前線程事務(wù)信息cleanupTransactionInfo(txInfo);}// 目標方法執(zhí)行成功,提交事務(wù)commitTransactionAfterReturning(txInfo);return retVal;} else {// 帶回調(diào)的事務(wù)執(zhí)行處理,一般用于編程式事務(wù)...} }在講Spring事務(wù)抽象時,有提到事務(wù)抽象的核心接口為PlatformTransactionManager,它負責管理事務(wù)行為,包括事務(wù)的獲取、提交和回滾。在invokeWithinTransaction方法中,我們可以看到createTransactionIfNecessary、commitTransactionAfterReturning和completeTransactionAfterThrowing都是針對該接口編程,并不依賴于特定事務(wù)管理器,這里是對Spring事務(wù)抽象的實現(xiàn)。
//TransactionAspectSupport.class protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {...TransactionStatus status = null;if (txAttr != null) {if (tm != null) {// 獲取事務(wù)status = tm.getTransaction(txAttr);... }protected void commitTransactionAfterReturning(TransactionInfo txInfo) {if (txInfo != null && txInfo.hasTransaction()) {...// 提交事務(wù)txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());} }protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.hasTransaction()) {...if (txInfo.transactionAttribute.rollbackOn(ex)) {try {// 異常類型為回滾異常,執(zhí)行事務(wù)回滾txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}...} else {try {// 異常類型為非回滾異常,仍然執(zhí)行事務(wù)提交txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}... }protected final class TransactionInfo {private final PlatformTransactionManager transactionManager;...另外,在獲取事務(wù)時,AbstractPlatformTransactionManager#doBegin方法負責開啟新事務(wù),在DataSourceTransactionManager有如下代碼:
@Override protected void doBegin(Object transaction, TransactionDefinition definition) {// 獲取數(shù)據(jù)庫連接con...if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}... }這里才真正開啟了數(shù)據(jù)庫事務(wù)。
Spring事務(wù)同步
提到事務(wù)傳播機制時,我們經(jīng)常提到一個條件“如果當前已有事務(wù)”,那么Spring是如何知道當前是否已經(jīng)開啟了事務(wù)呢?在AbstractPlatformTransactionManager中是這樣做的:
// AbstractPlatformTransactionManager.class @Override public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {Object transaction = doGetTransaction();// 參數(shù)為null時構(gòu)造默認值...if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(definition, transaction, debugEnabled);}...// 獲取當前事務(wù)對象 protected abstract Object doGetTransaction() throws TransactionException;// 判斷當前事務(wù)對象是否包含活躍事務(wù) protected boolean isExistingTransaction(Object transaction) throws TransactionException {return false; }注意getTransaction方法是final的,無法被子類覆蓋,保證了獲取事務(wù)流程的一致和穩(wěn)定。抽象方法doGetTransaction獲取當前事務(wù)對象,方法isExistingTransaction判斷當前事務(wù)對象是否存在活躍事務(wù),具體邏輯由特定事務(wù)管理器實現(xiàn),看下我們使用最多的DataSourceTransactionManager對應(yīng)的實現(xiàn):
// DataSourceTransactionManager.class @Override protected Object doGetTransaction() {DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());ConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);txObject.setConnectionHolder(conHolder, false);return txObject; }@Override protected boolean isExistingTransaction(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive()); }可以看到,獲取當前事務(wù)對象時,使用了TransactionSynchronizationManager#getResource方法,類圖如下:
?
TransactionSynchronizationManager通過ThreadLocal對象在當前線程記錄了resources和synchronizations屬性。resources是一個HashMap,用于記錄當前參與事務(wù)的事務(wù)資源,方便進行事務(wù)同步,在DataSourceTransactionManager的例子中就是以dataSource作為key,保存了數(shù)據(jù)庫連接,這樣在同一個線程中,不同的方法調(diào)用就可以通過dataSource獲取相同的數(shù)據(jù)庫連接,從而保證所有操作在一個事務(wù)中進行。synchronizations屬性是一個TransactionSynchronization對象的集合,AbstractPlatformTransactionManager類中定義了事務(wù)操作各個階段的調(diào)用流程,以事務(wù)提交為例:
// AbstractPlatformTransactionManager.class private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {prepareForCommit(status);triggerBeforeCommit(status);triggerBeforeCompletion(status);....else if (status.isNewTransaction()) {// 記錄日志...doCommit(status);}...// 事務(wù)調(diào)用異常處理...try {triggerAfterCommit(status);}finally {triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}} }我們可以看到,有很多trigger前綴的方法,這些方法用于在事務(wù)操作的各個階段觸發(fā)回調(diào),從而可以精確控制在事務(wù)執(zhí)行的不同階段所要執(zhí)行的操作,這些回調(diào)實際上都通過TransactionSynchronizationUtils來實現(xiàn),它會遍歷TransactionSynchronizationManager#synchronizations集合中的TransactionSynchronization對象,然后分別觸發(fā)集合中各個元素對應(yīng)方法的調(diào)用。例如:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// do something after commit} });這段代碼就在當前線程的事務(wù)synchronizations屬性中,添加了一個自定義同步類,如果當前存在事務(wù),那么在事務(wù)管理器執(zhí)行事務(wù)提交之后,就會觸發(fā)afterCommit方法,可以通過這種方式在事務(wù)執(zhí)行的不同階段自定義一些操作。
到這里,我們已經(jīng)對Spring事務(wù)的實現(xiàn)原理和處理流程有了一定的了解。
總結(jié)
以上是生活随笔為你收集整理的Spring事务原理一探的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android开发需要了解的 IM 知识
- 下一篇: 【入门】WebRTC知识点概览 | 内有