Spring宣告式事務管理原始碼解析
核心介面
1. PlatformTransactionManager
:事務管理器頂級介面:各持久化框架要想接入Spring的事務管理,必須自行提供該介面實現
2. TransactionDefinition
:事務的屬性頂級介面:其實現類封裝事務隔離級別、事務傳播性、只讀性等資訊
3. TransactionStatus
:事務狀態頂級介面:其實現類封裝事務狀態資訊
宣告式事務流程
說明 :以下流程基於JDBC事務管理,Hibernate或Mybatis事務流程與本流程類似。若流程詳解中程式碼片段有多段,均按執行時呼叫關係貼出,關鍵程式碼處均加上了註釋,從上到下閱讀即可。
Spring資料來源及事務配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd" >
<context:component-scan base-package="com.mercy">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<!-- 資料來源管理器-->
<bean id="dataSource" class="com.mercy.dynamic.DynamicDataSource"></bean>
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置宣告式事務-->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<!-- 配置程式設計式事務 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
</beans>
TransactionInterceptor中的流程
程式碼:
(TransactionInterceptor)
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// 獲取事務屬性及事務管理器
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 {
// proceedWithInvocation方法中,最終呼叫service處理邏輯
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 出現異常,繼續判斷是否需要回滾
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 重置事務資訊物件
cleanupTransactionInfo(txInfo);
}
// 呼叫service處理後正常返回,進行事務提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 省略部分程式碼...
}
}
- 獲取事務屬性及事務管理器,這裡獲取到的事務管理器就是在Spring配置檔案中配置的事務管理器例項
- 呼叫事務管理器的getTransaction()方法開啟事務,該方法引數是事務屬性,返回值是事務狀態,並把事務屬性、事務狀態、事務管理器封裝為事務資訊(TransactionInfo)返回,便於後續使用
- 呼叫service處理
- 若步驟3中丟擲異常
4.1 判斷異常是否是滿足回滾規則的異常,預設情況下,只有unchecked異常可回滾
4.2 滿足回滾規則,進行後續回滾處理
4.3 重置事務資訊物件 - 若步驟3中正常返回
5.1 重置事務資訊物件
5.2 開始進行提交事務
事務管理器中核心流程 –> 事務攔截器中各個流程的細節
由於我配置的是JDBC事務管理器,這裡也是針對此管理器敘述
1. 開啟事務 –> 事務攔截器的流程2
在事務管理器的細節
1.1 從資料來源中獲取資料庫連線
1.2 關閉自動提交
1.3 把獲取到的資料庫連線繫結到當前執行緒
2. 提交事務 –> 事務攔截器的流程5.2
在事務管理器的細節
2.1 從事務狀態物件中獲取資料庫連線
2.2 提交事務
3. 回滾事務 –> 事務攔截器的流程4.2
在事務管理器的細節
3.1 從事務狀態物件中獲取資料庫連線
3.2 回滾事務
4. 事務完成後的清理工作,在事務回滾或者事務提交後都會執行,具體時機見下面的詳解程式碼片段
4.1 解綁與當前執行緒繫結的資料來源
4.2 重置此次事務使用資料庫連線
4.3 釋放此次事務使用的資料庫連線,若沒有使用資料庫連線池,否則直接關閉,否則把資料庫連線歸還給連線池
TransactionInterceptor流程2詳解
程式碼1:
(TransactionInterceptor)
protected TransactionInfo createTransactionIfNecessary(
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// 省略部分程式碼...
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 通過配置的事務管理器開啟事務,返回事務狀態
status = tm.getTransaction(txAttr);
}
else {
// 省略部分程式碼...
}
}
// 把事務屬性、事務管理器、事務狀態封裝為事務資訊返回
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
程式碼2:
(DataSourceTransactionManager)
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// 省略部分程式碼...
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 開啟事務
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
// 省略部分程式碼...
}
程式碼3:
(DataSourceTransactionManager)
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (txObject.getConnectionHolder() == null ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 通過資料來源獲取資料庫連線
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 省略部分程式碼...
// 關閉自動提交
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 省略部分程式碼...
// 把資料庫連線繫結到當前執行緒
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
// 省略部分程式碼...
}
執行至此然後返回,整個事務開啟完成。這裡開啟事務的核心就是從配置的資料來源中獲取一個數據庫連線,關閉自動提交,然後把這個資料庫連線繫結到當前執行緒,供後續使用
TransactionInterceptor流程4詳解(呼叫service過程中出異常)
程式碼1:
(TransactionInterceptor)
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
// 省略部分程式碼...
// 判斷異常是否是滿足回滾規則的異常,預設情況下,只有unchecked異常可回滾
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 從事務資訊物件獲取事務管理器物件,進行事務回滾
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
// 省略部分程式碼...
}
else {
// 省略部分程式碼...
}
}
}
這裡核心就是判斷異常是否滿足回滾規則,若滿足則進行後續回滾
程式碼2:
(DataSourceTransactionManager)
private void processRollback(DefaultTransactionStatus status) {
try {
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
// 省略部分程式碼...
}
else if (status.isNewTransaction()) {
// 省略部分程式碼...
// 通過配置的事務管理器進行回滾
doRollback(status);
}
// 省略部分程式碼...
}
// 省略部分程式碼...
}
finally {
// 執行回滾的後需清理工作
cleanupAfterCompletion(status);
}
}
這裡先通過事務管理器進行回滾,回滾操作完成後進行清理工作,清理細節見後續程式碼4
程式碼3:
(DataSourceTransactionManager)
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
// 獲取之前已開啟事務的資料庫連線
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 回滾
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
這裡獲取之前開啟事務的資料庫連線進行回滾
程式碼4:
(DataSourceTransactionManager)
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 解綁與當前執行緒繫結的資料來源
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
// 重置資料庫連線
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
// 釋放資料庫連線
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
回滾後的清理工作:1.解綁與當前執行緒繫結的資料庫連線 2.重置資料庫連線 3.釋放資料庫連線
TransactionInterceptor流程5詳解(呼叫service過程正常返回)
程式碼1:
(TransactionInterceptor)
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
// 從事務資訊物件中獲取事務管理器,進行後續提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
程式碼2:
(DataSourceTransactionManager)
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
// 省略部分程式碼...
if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
// 執行提交
doCommit(status);
}
// 省略部分程式碼...
}
// 省略部分程式碼...
}
finally {
// 提交後的清理工作
cleanupAfterCompletion(status);
}
}
事務管理器進行事務提交,然後進行清理工作
程式碼3:
(DataSourceTransactionManager)
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
// 獲取之前開啟事務的資料庫連線
Connection con = txObject.getConnectionHolder().getConnection()
try {
// 提交事務
con.commit();
}
// 省略部分程式碼...
}
獲取之前開啟了事務的資料庫連線進行提交
程式碼4:
(DataSourceTransactionManager)
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 解綁與當前執行緒繫結的資料來源
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
// 重置資料庫連線
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
// 釋放資料庫連線
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
提交後的清理工作:1.解綁與當前執行緒繫結的資料庫連線 2.重置資料庫連線 3.釋放資料庫連線