日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

【技术干货】Spring事务原理一探

發(fā)布時間:2025/3/8 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【技术干货】Spring事务原理一探 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本篇文章是網(wǎng)易云信研發(fā)工程師對Spring事務實現(xiàn)原理及實現(xiàn)的研究和總結(jié),分享給大家,希望和大家共同探討。

事務是一個由有限操作集合組成的邏輯單元。事務操作包含兩個目的,數(shù)據(jù)一致以及操作隔離。數(shù)據(jù)一致是指事務提交時保證事務內(nèi)的所有操作都成功完成,并且更改永久生效;事務回滾時,保證能夠恢復到事務執(zhí)行之前的狀態(tài)。操作隔離則是指多個同時執(zhí)行的事務之間應該相互獨立,互不影響。

事務是一個比較廣泛的概念,事務管理資源除了我們熟知的數(shù)據(jù)庫外,還可以包含消息隊列、文件系統(tǒng)等。當然,一般來說,我們說的事務單指“數(shù)據(jù)庫事務”。接下來我們會以MySQL數(shù)據(jù)庫、Spring聲明式事務為主要研究對象,但是很多事務概念、接口抽象和實現(xiàn)方式同時適用于其他情況。

事務屬性和行為

ACID屬性

提到事務,不可避免需要涉及到事務的ACID屬性:

  • 原子性(Atomicity):事務作為一個整體被執(zhí)行,包含在其中的對數(shù)據(jù)庫的操作要么全部被執(zhí)行,要么都不執(zhí)行。

  • 一致性(Consistency):事務應確保數(shù)據(jù)庫的狀態(tài)從一個一致狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€一致狀態(tài)。一致狀態(tài)的含義是數(shù)據(jù)庫中的數(shù)據(jù)應滿足完整性約束。

  • 隔離性(Isolation):多個事務并發(fā)執(zhí)行時,一個事務的執(zhí)行不應影響其他事務的執(zhí)行。

  • 持久性(Durability):已被提交的事務對數(shù)據(jù)庫的修改應該永久保存在數(shù)據(jù)庫中。

我們將嚴格遵循ACID屬性的事務稱為剛性事務。與之相對,期望最終一致性,在事務執(zhí)行的中間狀態(tài)允許暫時不遵循ACID屬性的事務稱為柔性事務,可參考《傳統(tǒng)事務與柔性事務》(https://www.jianshu.com/p/ab1a1c6b08a1),柔性事務的使用涉及到分布式事務方案,可以后續(xù)擴展,這里我們先將注意集中在事務實現(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的幻讀問題》(http://blog.sina.com.cn/s/blog_499740cb0100ugs7.html)。

傳播機制

Spring針對方法嵌套調(diào)用時事務的創(chuàng)建行為定義了七種事務傳播機制,分別是PROPAGATIONREQUIRED、PROPAGATIONSUPPORT、PROPAGATIONMANDATORY、PROPAGATIONREQUIRESNEW、PROPAGATIONNOTSUPPORTED、PROPAGATIONNEVER以及PROPAGATION_NESTED,基本上從字面意思就能知道每種傳播機制各自的行為表現(xiàn),Spring默認的事務傳播機制是 PROPAGATION_REQUIRED,即如果當前存在事務,則使用當前事務,否則創(chuàng)建新的事務。詳情可參考《Spring事務傳播行為》(https://juejin.im/post/5ae9639af265da0b926564e7)。

事務行為

事務的行為包括事務開啟、事務提交和事務回滾。InnoDB所有的用戶SQL執(zhí)行都在事務控制之內(nèi),在默認情況下,autocommit設置為 true,單條SQL執(zhí)行成功后,MySQL會自動提交事務,或者如果SQL執(zhí)行出錯,則根據(jù)異常類型執(zhí)行事務提交或者回滾??梢允褂?START TRANSACTION(SQL標準)或者 BEGIN開啟事務,使用 COMMIT和 ROLLBACK提交和回滾事務;也可以通過設置autocommit屬性來控制事務行為,當設置autocommit為 false時,其后執(zhí)行的多條SQL語句將在一個事務內(nèi),直到執(zhí)行 COMMIT或者 ROLLBACK事務才會提交或者回滾。

AOP增強

Spring使用AOP(面向切面編程)來實現(xiàn)聲明式事務,后續(xù)在講Spring事務具體實現(xiàn)的時候會詳細說明,關(guān)于AOP的概念可參考《Spring AOP概念理解》(通俗易懂)(https://juejin.im/post/5ae9639af265da0b926564e7),這里不再細說。說下動態(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):為目標類添加新的方法和屬性。

聲明式事務的實現(xiàn)就是通過環(huán)繞增強的方式,在目標方法執(zhí)行之前開啟事務,在目標方法執(zhí)行之后提交或者回滾事務,事務攔截器的繼承關(guān)系圖可以體現(xiàn)這一點:

Spring事務抽象

統(tǒng)一一致的事務抽象是Spring框架的一大優(yōu)勢,無論是全局事務還是本地事務,JTA、JDBC、Hibernate還是JPA,Spring都使用統(tǒng)一的編程模型,使得應用程序可以很容易地在全局事務與本地事務,或者不同的事務框架之間進行切換。下圖是Spring事務抽象的核心類圖:

接口 PlatformTransactionManager定義了事務操作的行為,其依賴 TransactionDefinition和 TransactionStatus接口,其實大部分的事務屬性和行為我們以MySQL數(shù)據(jù)庫為例已經(jīng)有過了解,這里再對應介紹下。

  • PlatformTransactionManager:事務管理器

  • getTransaction方法:事務獲取操作,根據(jù)事務屬性定義,獲取當前事務或者創(chuàng)建新事物;

  • commit方法:事務提交操作,注意這里所說的提交并非直接提交事務,而是根據(jù)當前事務狀態(tài)執(zhí)行提交或者回滾操作;

  • rollback方法:事務回滾操作,同樣,也并非一定直接回滾事務,也有可能只是標記事務為只讀,等待其他調(diào)用方執(zhí)行回滾。

  • TransactionDefinition:事務屬性定義

  • getPropagationBehavior方法:返回事務的傳播屬性,默認是?PROPAGATION_REQUIRED;

  • getIsolationLevel方法:返回事務隔離級別,事務隔離級別只有在創(chuàng)建新事務時才有效,也就是說只對應傳播屬性?PROPAGATION_REQUIRED和?PROPAGATION_REQUIRES_NEW;

  • getTimeout方法:返回事務超時時間,以秒為單位,同樣只有在創(chuàng)建新事務時才有效;

  • isReadOnly方法:是否優(yōu)化為只讀事務,支持這項屬性的事務管理器會將事務標記為只讀,只讀事務不允許有寫操作,不支持只讀屬性的事務管理器需要忽略這項設置,這一點跟其他事務屬性定義不同,針對其他不支持的屬性設置,事務管理器應該拋出異常。

  • getName方法:返回事務名稱,聲明式事務中默認值為“類的完全限定名.方法名”。

  • TransactionStatus:當前事務狀態(tài)

  • isNewTransaction方法:當前方法是否創(chuàng)建了新事務(區(qū)別于使用現(xiàn)有事務以及沒有事務);

  • hasSavepoint方法:在嵌套事務場景中,判斷當前事務是否包含保存點;

  • setRollbackOnly和?isRollbackOnly方法:只讀屬性設置(主要用于標記事務,等待回滾)和查詢;

  • flush方法:刷新底層會話中的修改到數(shù)據(jù)庫,一般用于刷新如Hibernate/JPA的會話,是否生效由具體事務資源實現(xiàn)決定;

  • isCompleted方法:判斷當前事務是否已完成(已提交或者已回滾)。

部分Spring包含的對 PlatformTransactionManager的實現(xiàn)類如下圖所示:

AbstractPlatformTransactionManager抽象類實現(xiàn)了Spring事務的標準流程,其子類 DataSourceTransactionManager是我們使用較多的JDBC單數(shù)據(jù)源事務管理器,而 JtaTransactionManager是JTA(Java Transaction API)規(guī)范的實現(xiàn)類,另外兩個則分別是JavaEE容器WebLogic和WebSphere的JTA事務管理器的具體實現(xiàn)。

Spring事務切面

之前提到,Spring采用AOP來實現(xiàn)聲明式事務,那么事務的AOP切面是如何織入的呢?這一點涉及到AOP動態(tài)代理對象的生成過程。

代理對象生成的核心類是 AbstractAutoProxyCreator,實現(xiàn)了 BeanPostProcessor接口,會在Bean初始化完成之后,通過 postProcessAfterInitialization方法生成代理對象,關(guān)于 BeanPostProcessor在Bean生命周期中的作用,可參考《一些常用的Spring擴展接口》(https://www.cnblogs.com/xrq730/p/5721366.html)。

看一下 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)代理時,設置是否需要對目標Bean強制使用CGLIB動態(tài)代理...// 構(gòu)建AOP增強顧問,包含框架公共增強和應用程序自定義增強// 設置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屬性設置為 true或者未指定需要代理的接口,則使用CGLIB生成代理對象,否則使用JDK動態(tài)代理。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 如果optimize,proxyTargetClass屬性設置為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事務攔截

我們已經(jīng)了解了AOP切面織入生成代理對象的過程,當Bean方法通過代理對象調(diào)用時,會觸發(fā)對應的AOP增強攔截器,前面提到聲明式事務是一種環(huán)繞增強,對應接口為 MethodInterceptor,事務增強對該接口的實現(xiàn)為 TransactionInterceptor,類圖如下:

事務攔截器 TransactionInterceptor在 invoke方法中,通過調(diào)用父類 TransactionAspectSupport的 invokeWithinTransaction方法進行事務處理,該方法支持聲明式事務和編程式事務。

// 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.// 查詢目標方法事務屬性、確定事務管理器、構(gòu)造連接點標識(用于確認事務名稱)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)) {// 事務獲取TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 通過回調(diào)執(zhí)行目標方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 目標方法執(zhí)行拋出異常,根據(jù)異常類型執(zhí)行事務提交或者回滾操作completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// 清理當前線程事務信息cleanupTransactionInfo(txInfo);}// 目標方法執(zhí)行成功,提交事務commitTransactionAfterReturning(txInfo);return retVal;} else {// 帶回調(diào)的事務執(zhí)行處理,一般用于編程式事務...} }

在講Spring事務抽象時,有提到事務抽象的核心接口為 PlatformTransactionManager,它負責管理事務行為,包括事務的獲取、提交和回滾。在 invokeWithinTransaction方法中,我們可以看到 createTransactionIfNecessary、 commitTransactionAfterReturning和 completeTransactionAfterThrowing都是針對該接口編程,并不依賴于特定事務管理器,這里是對Spring事務抽象的實現(xiàn)。

//TransactionAspectSupport.class protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {...TransactionStatus status = null;if (txAttr != null) {if (tm != null) {// 獲取事務status = tm.getTransaction(txAttr);... } protected void commitTransactionAfterReturning(TransactionInfo txInfo) {if (txInfo != null && txInfo.hasTransaction()) {...// 提交事務txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());} } protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.hasTransaction()) {...if (txInfo.transactionAttribute.rollbackOn(ex)) {try {// 異常類型為回滾異常,執(zhí)行事務回滾txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());}...} else {try {// 異常類型為非回滾異常,仍然執(zhí)行事務提交txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());}... } protected final class TransactionInfo {private final PlatformTransactionManager transactionManager;...

另外,在獲取事務時, AbstractPlatformTransactionManager#doBegin方法負責開啟新事務,在 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ù)庫事務。

Spring事務同步

提到事務傳播機制時,我們經(jīng)常提到一個條件“如果當前已有事務”,那么Spring是如何知道當前是否已經(jīng)開啟了事務呢?在 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);}... // 獲取當前事務對象 protected abstract Object doGetTransaction() throws TransactionException; // 判斷當前事務對象是否包含活躍事務 protected boolean isExistingTransaction(Object transaction) throws TransactionException {return false; }

注意 getTransaction方法是 final的,無法被子類覆蓋,保證了獲取事務流程的一致和穩(wěn)定。抽象方法 doGetTransaction獲取當前事務對象,方法 isExistingTransaction判斷當前事務對象是否存在活躍事務,具體邏輯由特定事務管理器實現(xiàn),看下我們使用最多的 DataSourceTransactionManager對應的實現(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()); }

可以看到,獲取當前事務對象時,使用了 TransactionSynchronizationManager#getResource方法,類圖如下:

TransactionSynchronizationManager通過 ThreadLocal對象在當前線程記錄了 resources和 synchronizations屬性。 resources是一個HashMap,用于記錄當前參與事務的事務資源,方便進行事務同步,在 DataSourceTransactionManager的例子中就是以 dataSource作為key,保存了數(shù)據(jù)庫連接,這樣在同一個線程中,不同的方法調(diào)用就可以通過 dataSource獲取相同的數(shù)據(jù)庫連接,從而保證所有操作在一個事務中進行。 synchronizations屬性是一個 TransactionSynchronization對象的集合, AbstractPlatformTransactionManager類中定義了事務操作各個階段的調(diào)用流程,以事務提交為例:

// 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);}...// 事務調(diào)用異常處理...try {triggerAfterCommit(status);}finally {triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);}} }

我們可以看到,有很多trigger前綴的方法,這些方法用于在事務操作的各個階段觸發(fā)回調(diào),從而可以精確控制在事務執(zhí)行的不同階段所要執(zhí)行的操作,這些回調(diào)實際上都通過 TransactionSynchronizationUtils來實現(xiàn),它會遍歷 TransactionSynchronizationManager#synchronizations集合中的 TransactionSynchronization對象,然后分別觸發(fā)集合中各元素對應方法的調(diào)用。例如:

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// do something after commit} });

這段代碼就在當前線程的事務 synchronizations屬性中,添加了一個自定義同步類,如果當前存在事務,那么在事務管理器執(zhí)行事務提交之后,就會觸發(fā) afterCommit方法,可以通過這種方式在事務執(zhí)行的不同階段自定義一些操作。

到這里,我們已經(jīng)對Spring事務的實現(xiàn)原理和處理流程有了一定的了解。

更多技術(shù)干貨分享,點擊【閱讀原文】。

總結(jié)

以上是生活随笔為你收集整理的【技术干货】Spring事务原理一探的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。