一、Spring事務使用
1.通過maven方式引入jar包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.20.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.20.RELEASE</version>
</dependency>
2.創建配置類,使用注解@EnableTransactionManagement開啟事務功能,代碼如下:
package com.ybe.transaction.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@ComponentScan("com.ybe.transaction")
public class TransactionConfig {
@Bean
public DataSource dataSource() {
DruidDataSource data = https://www.cnblogs.com/yuanbeier/p/new DruidDataSource();
data.setDriverClassName("com.mysql.cj.jdbc.Driver");
data.setUrl("jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
data.setUsername("xxx");
data.setPassword("xxx");
return data;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.使用@Transactional注解,代碼如下:
package com.ybe.transaction.service;
import com.ybe.transaction.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
@Autowired
BookDao bookDao;
public BookDao getBookDao() {
return bookDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
/**
* 結賬:傳入哪個用戶買了哪本書
* @param id
*/
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(int id){
bookDao.updateStock(id);
}
}
package com.ybe.transaction.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 減庫存,減去某本書的庫存
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
// for (int i = 1 ;i>=0 ;i--)
// System.out.println(10/i);
}
}
5.Main的代碼
AnnotationConfigApplicationContext configApplicationContext = new AnnotationConfigApplicationContext(TransactionConfig.class);
BookService bean = configApplicationContext.getBean(BookService.class);
bean.checkout(1);
System.out.println("完成");
三、Spring事務的原始碼分析
Spring事務和SpringAOP一樣,都是利用了SpringIoc容器的插件功能,在SpringIOC容器的生命周期程序中整合了Spring事務的功能,大概程序:通過 @Import注冊 TransactionManagementConfigurationSelector 類,該類中實作了 ImportSelector介面的selectImports方法 ,在創建AnnoteationConfigApplicationContext的建構式中會呼叫refresh()方法,在 invokeBeanFactoryPostProcessors()中會呼叫TransactionManagementConfigurationSelector 類的 selectImports 方法,會生成 AutoProxyRegistrar 的物件和ProxyTransactionManagementConfiguration物件,AutoProxyRegistrar會呼叫該類的registerBeanDefinitions方法注冊型別為InfrastructureAdvisorAutoProxyCreator的RootBeanDefinition,ProxyTransactionManagementConfiguration中注入了BeanFactoryTransactionAttributeSourceAdvisor型別的Bean,即事務通知類,該類繼承了 Advisor, 在finishBeanFactoryInitialization()程序中第一次呼叫 getAdvicesAndAdvisorsForBean 的時候,會先進行Advisors的創建,再獲取候選的Advisors,在創建完 Bean后會呼叫InfrastructureAdvisorAutoProxyCreator的 postProcessAfterInitialization方法,從 Advisors 中查找是否匹配當前正在創建的Bean,如果能匹配,則創建相關的事務動態代理物件,
完整原始碼分析分三部分:Spring事務的初始化、事務的創建、事務的呼叫程序,
一、Spring事務的初始化
主要邏輯是找到所有標注了 @EnableTransactionManagement的類,并且決議標注中的事務通知方法和BeanPostProcessor的實作類BeanFactoryTransactionAttributeSourceAdvisor,
整體代碼流程圖如下:

說明:
-
創建 AnnotationConfigApplicationContext() 容器,
-
在invokeBeanFactoryPostProcessors()中,會呼叫 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry() ,在此方法中,會找到 @EnableTransactionManagement的 @Import 屬性傳入的 TransactionManagementConfigurationSelector.class 類,并且執行該類的selectImports() 方法,將型別為 InfrastructureAdvisorAutoProxyCreator、名稱為org.springframework.aop.
config.internalAutoProxyCreator和型別為 ProxyTransactionManagementConfiguration的 RootBeanDefinition注冊到BeanDefinitionRegistry中, -
在 registerBeanPostProcessors() 中會根據上面一步生成的 RootBeanDefinition物件創建 InfrastructureAdvisorAutoProxyCreator的Bean實體,
-
在 finishBeanFactoryInitialization() 中第一次執行到 、AbstractAutowireCapableBeanFactory.initializeBean() 時,會執行一段這樣的代碼,如下
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
以上代碼會執行 InfrastructureAdvisorAutoProxyCreator 的 postProcessAfterInitialization() 方法,代碼如下:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
在 wrapIfNecessary 的getAdvicesAndAdvisorsForBean會呼叫findEligibleAdvisors,代碼如下:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
在findCandidateAdvisors方法中會生成所有的 Advisors,其中事務的Advisor 是 BeanFactoryTransactionAttributeSourceAdvisor 類,
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
advisorRetrievalHelper.findAdvisorBeans()是關鍵,方法里面的邏輯如下:
1.先從cachedAdvisorBeanNames中獲取beanNames
2.如果cachedAdvisorBeanNames為null,
2.1 則獲取所有型別為 Advisor的 beanNames,并且添加到cachedAdvisorBeanNames中
3. 遍歷 beanNames ,根據beanName獲取具體的Bean物件,添加到advisors中
4.最后回傳 advisors,
advisorRetrievalHelper.findAdvisorBeans()方法會快取所有型別為Advisor的BeanName,方法的最后回傳 advisors,至此整個 Spring事務的初始化完成,
二、事務的創建
? 在創建Bean的生命周期的 initializeBean 方法中,會執行 InfrastructureAdvisorAutoProxyCreator的 postProcessAfterInitialization方法,該方法里會拿快取中BeanFactoryTransactionAttributeSourceAdvisor的pointCut去匹配正在創建的實體Bean的所有方法,如果 BeanFactoryTransactionAttributeSourceAdvisor 和 Bean 的某一個方法能匹配上,則把該BeanFactoryTransactionAttributeSourceAdvisor 添加到 advisor的候選集合中,直到找出匹配Bean的所有Adsivors,最后根據Adsivor的候選集合和Bean型別創建動態代理物件ProxyFactory,
整體代碼流程圖如下:

說明:
1.動態代理的創建
創建動態代理有兩種方法,一種是 JDK ,一種是 CGLib ,
1.如果目標類有實作介面的話,則是使用JDK的方式生成代理物件,
2.配置了使用Cglib進行動態代理或者目標類沒有實作介面,那么使用Cglib的方式創建代理物件,
區別:
創建:CGLIB由于創建多個.class 檔案所以 效率肯定要慢于JDK動態代理
呼叫:CGLIB是要優于JDK的,因為JDK使用的反射,而CGLIB直接呼叫,
三、事務的呼叫程序
? 以上面的Main方法為示例,只分析兩層事務,并且只分析事務提交成功的情況,在呼叫bean.checkout(1)方法的時候會直接呼叫 CglibAopProxy.intercept()方法,里面的大概邏輯如下:
-
獲取被代理的實作類;
-
找出匹配該checkout 的 BeanFactoryTransactionAttributeSourceAdvisor,并且把 BeanFactoryTransactionAttributeSourceAdvisor 轉成Interceptor型別,回傳攔截器鏈,
-
創建 CglibMethodInvocation (該物件中包括了 代理物件、被代理物件、執行的方法、方法引數、被代理物件的型別、事務攔截器)它繼承了 ReflectiveMethodInvocation 物件,執行CglibMethodInvocation 物件的proceed()方法,里面會呼叫 ReflectiveMethodInvocation 的 proceed方法 ,該方法中會進行事務攔截器的遞回呼叫,具體呼叫流程如下圖,ReflectiveMethodInvocation 物件在通知攔截器鏈呼叫中作用很關鍵,有銜接各個攔截器的作用,
代碼流程如下圖:

-
在ReflectiveMethodInvocation 的proceed方法中,會先判斷當前攔截器鏈的索引,如果索引等于最后一個那么則執行被代理類的方法,
-
如果不是,那么先從攔截器鏈中獲取攔截器(TransactionInterceptor)并且執行該攔截器的 proceed 方法(方法接受 ReflectiveMethodInvocation 物件實體)
-
TransactionInterceptor會呼叫 invokeWithinTransaction()方法,主要邏輯為
1. 是否需要創建事務, 2. 執行具體的方法, 3. 回滾或者提交事務,主要代碼如下:
//創建必要的事務
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 執行具體方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 回滾
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//事務提交
commitTransactionAfterReturning(txInfo);
- createTransactionIfNecessary 中會呼叫 getTransaction 方法,getTransaction 方法執行的關鍵代碼有
7.1 doGetTransaction(),創建資料源事務物件,并設定 conHolder物件,此時conHolder為null,
// 創建一個資料源事務物件
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允許當前事務設定保持點
txObject.setSavepointAllowed(isNestedTransactionAllowed());
/**
* TransactionSynchronizationManager 事務同步管理器物件(該類中都是區域執行緒變數)
* 用來保存當前事務的資訊,我們第一次從這里去執行緒變數中獲取 事務連接持有器物件 通過資料源為key去獲取
* 由于第一次進來開始事務 我們的事務同步管理器中沒有被存放.所以此時獲取出來的conHolder為null
*/
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 非新創建連接則寫false
txObject.setConnectionHolder(conHolder, false);
// 回傳事務物件
return txObject;
7.2 isExistingTransaction(transaction),判斷依據為當前執行緒記錄的連接不為空且連接中的transactionActive屬性不為空,此時為不存在連接,所以該方法回傳false,
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 若第一次進來開始事務,txObject.hasConnectionHolder()回傳的null那么表示不存在事務
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
? 7.3 因為checkout()方法 的 事務屬性為Propagation.REQUIRED,所以這里進入如下代碼
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
其中 suspend(null),表示沒有當前事務,因為這個時候當前的執行緒變數中有沒有激活的事物并且transaction 為null 所以這個方法回傳 null,代碼如下:
else {
// Neither transaction nor synchronization active.
return null;
}
? 7.4 startTransaction(),開啟事務并回傳事務狀態,
// 是否需要新同步
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 創建新的事務
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 開啟事務和連接
doBegin(transaction, definition);
// 同步事務的設定,如果執行緒狀態是:NewSynchronization,則設定執行緒同步管理器的相關資訊
prepareSynchronization(status, definition);
return status;
? 其中 doBegin(),創建連接,關閉自動提交,保存連接持有器到當前執行緒管理器中(確保嵌套中的事務屬性物件可以訪問到當前的事務連接器,即事務連接可傳播,內層的事務可以訪問外層的事務),其中 doBegin方法中關鍵代碼如下:
// 通過資料源獲取一個資料庫連接物件
Connection newCon = obtainDataSource().getConnection();
// 把我們的資料庫連接包裝成一個ConnectionHolder物件 然后設定到我們的txObject物件中去
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
// 關閉自動提交
con.setAutoCommit(false);
//如果是新事務連接持有器則保存連接持有器到執行緒管理器中
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
}
? 其中 prepareSynchronization,同步事務的設定,如果執行緒狀態是:NewSynchronization,則設定執行緒同步管理器的相關資訊,此時執行緒狀態是NewSynchronization,代碼如下:
if (status.isNewSynchronization()) {
// 系結事務激活
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
// 當前事務的隔離級別
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
// 是否為只讀事務
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
// 事務的名稱
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
- createTransactionIfNecessary 中的prepareTransactionInfo(),創建一個TransactionInfo,然后設定該事務資訊的新事務狀態,代碼如下:
//創建事務資訊物件
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// The transaction manager will flag an error if an incompatible tx already exists.
//設定事務狀態
txInfo.newTransactionStatus(status);
}
- invocation.proceedWithInvocation()執行具體的方法體,這里指checkout()的方法,當執行 bookDao.updateStock(id)的時候,因為bookDao也是代理類,這里會進入第內層代理類的呼叫,即呼叫CglibAopProxy.intercept()方法,
以下代碼是 updateStock(id)方法的事務處理程序:
- 前面步驟和 checkout() 都差不多,直到createTransactionIfNecessary 的中getTransaction的代碼:
? 1.1 doGetTransaction(),創建資料源事務物件,并設定conHolder物件,由于是第二次進來創建事務方法, 我們的事務同步管理器中有第一次事務保存的conHolder,所以此時獲取出來的conHolder不為null,
// 創建一個資料源事務物件
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允許當前事務設定保持點
txObject.setSavepointAllowed(isNestedTransactionAllowed());
/**
* TransactionSynchronizationManager 事務同步管理器物件(該類中都是區域執行緒變數)
* 用來保存當前事務的資訊,我們第一次從這里去執行緒變數中獲取 事務連接持有器物件 通過資料源為key去獲取
* 由于第二次進來開始事務 我們的事務同步管理器中有第一次事務保存的conHolder.所以此時獲取出來的conHolder不為null
*/
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 非新創建連接則寫false
txObject.setConnectionHolder(conHolder, false);
? 1.2 isExistingTransaction(transaction),判斷依據為當前執行緒記錄的連接不為空且連接中的transactionActive屬性不為空,此時為存在,會執行if代碼塊里面的代碼,執行handleExistingTransaction()方法,在此方法中執行的關鍵代碼如下:
? 1.2.1 由于updateStock的事務屬性為Propagation.REQUIRES_NEW,所以執行下面的代碼:
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
return startTransaction(definition, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
? 1.2.2 suspend方法中主要邏輯為:呼叫doSuspend 掛起連接持有器,然后先從事務管理器中獲取當前事務(外層事務)的名稱、只讀屬性、事務隔離級別、當前事務激活狀態,然后清空事務狀態,再把之前獲取到的屬性封裝為一個掛起的事務屬性SuspendedResourcesHolder回傳出去,該事務屬性用來記錄當前事務的相關屬性,以便內層事務后面用來恢復外層事務,代碼如下:
// 判斷當前的執行緒同步管理器變數中有沒有激活的事物,有需要清空執行緒變數
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
//掛起的資源,連接持有器,這里指掛起當前連接,后面備用,會傳入掛起的資源管理器
suspendedResources = doSuspend(transaction);
}
// 獲取當前事務名稱
String name = TransactionSynchronizationManager.getCurrentTransactionName();
// 清空執行緒變數
TransactionSynchronizationManager.setCurrentTransactionName(null);
// 獲取出只讀事務的名稱
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
// 清空執行緒變數
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
// 獲取已存在事務的隔離級別
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
// 清空隔離級別
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
// 判斷當前事務激活狀態
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
// 清空標記
TransactionSynchronizationManager.setActualTransactionActive(false);
// 把上訴從執行緒變數中獲取出來的存在事務屬性封裝為掛起的事務屬性回傳出去
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
其中 doSuspend ,主要是掛起外層的事務連接持有器并回傳掛起的物件,代碼邏輯如下
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 清空連接持有器
txObject.setConnectionHolder(null);
// 解綁執行緒私有的資源,回傳給上一層
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
? 1.2.3 startTransaction 開啟事務并回傳事務狀態,這里會存入上面回傳的掛起的資源連接器,里面保存了外層事務狀態的相關資訊以及連接持有器,
// 是否需要新同步
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 創建新的事務狀態
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 開啟事務和連接
doBegin(transaction, definition);
// 同步事務的設定,如果執行緒狀態是:新執行緒,則設定執行緒同步管理器的相關資訊
prepareSynchronization(status, definition);
return status;
? 1.2.3.1 doBegin(),創建連接,關閉自動提交,保存連接持有器到當前執行緒管理器中(確保嵌套中的事務屬性物件可以訪問到當前的事務連接器,即事務連接可傳播,內層的事務可以訪問外層的事務),和上面的doBegin作用一樣,
? 1.2.3.2 prepareSynchronization(),同步事務的設定,如果執行緒狀態是:NewSynchronization,則設定執行緒同步管理器的相關資訊,此時執行緒狀態是NewSynchronization,
- createTransactionIfNecessary 中 prepareTransactionInfo(),創建一個TransactionInfo,然后設定該事務資訊的新事務狀態,
- retVal = invocation.proceedWithInvocation();執行updateStock(id)方法體里面的內容,
- commitTransactionAfterReturning 提交具體的事務,由于此時TransactionInfo的TransactionStatus為true,會執行如下代碼:
//當前狀態是新事務
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 如果是獨立的事務則直接提交
doCommit(status);
}
? 4.1 其中doCommitf方法中會獲取連接物件,并且完成資料庫事務的提交,代碼如下:
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
// JDBC連接提交
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
- cleanupAfterCompletion,回滾后的處理作業,其中resume方法中會恢復之前掛起的事務,代碼如下:
// 有掛起的事務要恢復
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
// 結束之前事務的掛起狀態
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
- resume 中的 doResume 會拿到外層的事務即(checkout方法事務)的連接持有器放入事務同步管理器中,這樣就確保了,在執行到 checkout方法體的時候拿到的連接物件是checkout當時生成的連接物件,以便checkout方法類其他嵌套事務呼叫,
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), suspendedResources);
}
至此,updateStock() 方法執行基本完成,代碼執行回到checkout()方法體中,
- checkout執行完畢之后,回到checkout程序中的invocation.proceedWithInvocation(),
- 如果成功,則commitTransactionAfterReturning 提交事務,由于此時TransactionInfo的TransactionStatus為true,會執行如下代碼:
//當前狀態是新事務
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 如果是獨立的事務則直接提交
doCommit(status);
}
? 2.1 其中doCommitf方法中會獲取連接物件,并且完成資料庫事務的提交,代碼如下:
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
// JDBC連接提交
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
至此,checkout() 方法執行大概完成,整個代碼呼叫事務也基本完成了,
四、事務的傳播行為總結
| 事務的傳播行為 | 外部不存在事務 | 外部存在事務 |
|---|---|---|
| REQUIRED(默認) | 開啟新事務 | 融合到外部事務中 |
| SUPPORTS | 不開啟新事務 | 融合到外部事務中 |
| MANDATORY | 拋出例外 | 融合到外部事務中 |
| REQUIRES_NEW | 開啟新事務 | 掛起外部事務,創建新事務 |
| NOT_SUPPORTED | 不開啟新事務 | 掛起外部事務 |
| NEVER | 不開啟新事務 | 拋出例外 |
| NESTED | 開啟新的事務 | 融合到外部事務中,SavePoint機制,外層影響內層,內層不會影響外層 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/474787.html
標籤:Java
