Spring事務源碼解讀
一、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 = 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機制,外層影響內層,內層不會影響外層 |