Spring事務源碼解析(聲明式事務)

切入點:

spring自定義標籤的解析

上面的resolve方法點進去,執行TxNamespaceHandler.init()方法

以上兩個parser分別用來解析tx:advice和tx:annotation-driven標籤。

進行標籤解析:

調用parser.parse()方法進行解析:

最外圍的if判斷限制了<tx:annotation-driven/>標籤只能被解析一次,所以只有第一次被解析的標籤會生效。藍色框的部分分別註冊了三個BeanDefinition,分別爲AnnotationTransactionAttributeSource、TransactionInterceptor和BeanFactoryTransactionAttributeSourceAdvisor,並將前兩個BeanDefinition添加到第三個BeanDefinition的屬性當中,這三個bean支撐了整個事務功能。

我們先來看紅色框的第一個方法:

還記得當<tx:annotation-driven/>標籤在不指定transaction-manager屬性的時候,會默認尋找id固定名爲transactionManager的bean作爲事務管理器這個注意事項麼,就是在這裏實現的。

下面我們來看紅色框的第二個方法:

這裏可以看到,註冊了一個InfrastructureAdvisorAutoProxyCreator類型的Bean。

其實InfrastructureAdvisorAutoProxyCreator是一個BeanPostProcessor

其父類AbstractAutoProxyCreator重寫了postProcessAfterInitialization,如下:

先來看方法1:

先找出所有對應Advisor的類的beanName,再通過beanFactory.getBean方法獲取這些bean並返回。

還記得前面提到的三個類,其中BeanFactoryTransactionAttributeSourceAdvisor實現了Advisor接口,所以這個bean就會在此被提取出來,而另外兩個bean被織入了BeanFactoryTransactionAttributeSourceAdvisor當中,所以也會一起被提取出來,下圖爲BeanFactoryTransactionAttributeSourceAdvisor類的層次:

下面讓我們來看Spring如何在所有候選的增強器中獲取匹配的增強器:

上面的方法中提到引介增強的概念,在此做簡要說明,引介增強是一種比較特殊的增強類型,它不是在目標方法周圍織入增強,而是爲目標類創建新的方法和屬性,所以引介增強的連接點是類級別的,而非方法級別的。通過引介增強,我們可以爲目標類添加一個接口的實現,即原來目標類未實現某個接口,通過引介增強可以爲目標類創建實現該接口的代理。另外這個方法用兩個重載的canApply方法爲目標類尋找匹配的增強器,其中第一個canApply方法會調用第二個canApply方法並將第三個參數傳爲false:

在上面BeanFactoryTransactionAttributeSourceAdvisor類的層次中我們看到它實現了PointcutAdvisor接口,所以會調用紅框中的canApply方法進行判斷,第一個參數pca.getPointcut()也就是調用BeanFactoryTransactionAttributeSourceAdvisor的getPointcut方法:

這裏的transactionAttributeSource也就是我們在文章開始看到的爲BeanFactoryTransactionAttributeSourceAdvisor織入的兩個bean中的AnnotationTransactionAttributeSource,我們以TransactionAttributeSourcePointcut作爲第一個參數繼續跟蹤canApply方法:

我們跟蹤pc.getMethodMatcher()方法也就是TransactionAttributeSourcePointcut的getMethodMatcher方法是在它的父類中實現:

發現方法直接返回this,也就是下面methodMatcher.matches方法就是調用TransactionAttributeSourcePointcut的matches方法:

在上面我們看到其實這個tas就是AnnotationTransactionAttributeSource,這裏的目的其實也就是判斷我們的業務方法或者類上是否有@Transactional註解,跟蹤AnnotationTransactionAttributeSource的getTransactionAttribute方法(在其父類AbstractFallbackTransactionAttributeSource中):

我們來看看上圖中的第一個方法 computeTransactionAttribute:

方法中的事務聲明優先級最高,如果方法上沒有聲明則在類上尋找:

this.annotationParsers是在AnnotationTransactionAttributeSource類初始化的時候初始化的:

所以annotationParser.parseTransactionAnnotation就是調用SpringTransactionAnnotationParser的parseTransactionAnnotation方法:

至此,我們終於看到的Transactional註解,下面無疑就是解析註解當中聲明的屬性了:

在這個方法中我們看到了在Transactional註解中聲明的各種常用或者不常用的屬性的解析,

至此,事務的初始化工作算是完成了,下面開始真正的進入執行階段。

以JDK動態代理爲例,調用的是JdkDynamicAopProxy的invoke()方法。

第一步:

獲取方法的攔截器鏈(所有要執行的 advice)

注:這裏的advisors是在後置處理的時候放進去的:

MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

這一步的作用是,獲取當前advisor的advice,即攔截器,這裏指的就是

BeanFactoryTransactionAttributeSourceAdvisor的advice->TransactionInterceptor

1.如果advice是MethodInterceptor類型,則直接添加;

2.如果advice不是MethodInterceptor類型,但是是通知適配器支持的類型,比如:

@Before  @AfterReturning  @AfterThrowing等類型的通知,則轉成MethodInterceptor類型。

第二步:

創建方法調用器,調用proceed()方法執行攔截器鏈

然後調用TransactionInterceptor.invoke()方法

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		if (this.reactiveAdapterRegistry != null) {
			ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
			if (adapter != null) {
				return new ReactiveTransactionSupport(adapter).invokeWithinTransaction(method, targetClass, invocation);
			}
		}

		// If the transaction attribute is null, the method is non-transactional.
		// 獲取事務屬性信息
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		// 確定transactionManager
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		// 獲取方法的全限定名
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			// 開啓事務
			TransactionInfo txInfo = createTransactionIfNecessary(tm, 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
				// catch到異常,則完成事務(提交或回滾事務)
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}

			if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
				// Set rollback-only in case of Vavr failure matching our rollback rules...
				TransactionStatus status = txInfo.getTransactionStatus();
				if (status != null && txAttr != null) {
					retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
				}
			}

			// 執行成功,則完成事務(提交事務)
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
					TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
					try {
						Object retVal = invocation.proceedWithInvocation();
						if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
							// Set rollback-only in case of Vavr failure matching our rollback rules...
							retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
						}
						return retVal;
					}
					catch (Throwable ex) {
						if (txAttr.rollbackOn(ex)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException) {
								throw (RuntimeException) ex;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							throwableHolder.throwable = ex;
							return null;
						}
					}
					finally {
						cleanupTransactionInfo(txInfo);
					}
				});

				// Check result state: It might indicate a Throwable to rethrow.
				if (throwableHolder.throwable != null) {
					throw throwableHolder.throwable;
				}
				return result;
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
			catch (TransactionSystemException ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
					ex2.initApplicationException(throwableHolder.throwable);
				}
				throw ex2;
			}
			catch (Throwable ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
				}
				throw ex2;
			}
		}
	}

 這個方法很長,但是整體邏輯還是非常清晰的,首選獲取事務屬性,這裏的getTransactionAttrubuteSource()方法的返回值同樣是在開始我們看到的被織入到TransactionInterceptor中的AnnotationTransactionAttributeSource,在事務準備階段已經解析過事務屬性並保存到緩存中,所以這裏會直接從緩存中獲取,接下來獲取配置的TransactionManager,也就是determineTransactionManager方法,這裏如果配置沒有指定transaction-manager並且也沒有默認id名爲transactionManager的bean,就會報錯,然後是針對聲明式事務和編程式事務的不同處理,創建事務信息,執行目標方法,最後根據執行結果進行回滾或提交操作。

我們先分析創建事務的過程。在分析之前希望大家能先去了解一下Spring的事務傳播行爲,有助於理解下面的源碼,

這裏做一個簡要的介紹,更詳細的信息請大家自行查閱Spring官方文檔,裏面有更新詳細的介紹。

Spring的事務傳播行爲定義在Propagation這個枚舉類中,一共有七種,分別爲:

REQUIRED:支持使用當前事務,如果當前事務不存在,則自己新建一個新的事務,是默認的事務傳播行爲

NOT_SUPPORTED:不使用事務,如果當前存在事務,則將當前事務掛起。

REQUIRES_NEW:創建一個新的事務,如果當前事務已存在,則將當前事務掛起。

MANDATORY:有強制的意思,支持使用當前事務,如果事務不存在,則直接拋出Exception。

SUPPORTS:支持使用當前事務,如果當前事務不存在,則不使用事務。

NEVER:無事務,如果存在事務,則直接拋出Exception。

NESTED:如果當前存在事務,則在嵌套的事務中運行。如果當前沒有事務,則等同於REQUIRED。內部事務的回滾不會對外部事務造成影響,

它只對DataSourceTransactionManager事務管理器起效。

下面我們來分析事務創建的過程:

先來看第一個方法:

第一個方法doGetTransaction:

org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction

第二個方法isExistingTransaction:

org.springframework.jdbc.datasource.DataSourceTransactionManager#isExistingTransaction

判斷當前線程是否存在事務就是判斷記錄的數據庫連接是否爲空並且transactionActive狀態爲true。

第三個方法handleExistingTransaction:

private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}

		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}

		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 {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				// 開啓事務
				doBegin(transaction, definition);
				// 同步事務狀態
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException | Error beginEx) {
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
		}

		// 嵌套事務處理
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			if (!isNestedTransactionAllowed()) {
				throw new NestedTransactionNotSupportedException(
						"Transaction manager does not allow nested transactions by default - " +
						"specify 'nestedTransactionAllowed' property with value 'true'");
			}
			if (debugEnabled) {
				logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
			}
			if (useSavepointForNestedTransaction()) {
				// Create savepoint within existing Spring-managed transaction,
				// through the SavepointManager API implemented by TransactionStatus.
				// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
				DefaultTransactionStatus status =
						prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
				status.createAndHoldSavepoint();
				return status;
			}
			else {
				// Nested transaction through nested begin and commit/rollback calls.
				// Usually only for JTA: Spring synchronization might get activated here
				// in case of a pre-existing JTA transaction.
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, null);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
		}

		// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
		if (debugEnabled) {
			logger.debug("Participating in existing transaction");
		}
		if (isValidateExistingTransaction()) {
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
				Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
					Constants isoConstants = DefaultTransactionDefinition.constants;
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] specifies isolation level which is incompatible with existing transaction: " +
							(currentIsolationLevel != null ?
									isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
									"(unknown)"));
				}
			}
			if (!definition.isReadOnly()) {
				if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
					throw new IllegalTransactionStateException("Participating transaction with definition [" +
							definition + "] is not marked as read-only but existing transaction is");
				}
			}
		}
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
	}

REQUIRESNEW會開啓一個新事務並掛起原事務,當然開啓一個新事務就需要一個新的數據庫連接:

suspend掛起操作主要目的是將當前connectionHolder置爲null,保存原有事務信息,以便於後續恢復原有事務,

並將當前正在進行的事務信息進行重置。下面我們看Spring如何開啓一個新事務:

這裏我們看到了數據庫連接的獲取,如果是新事務需要獲取新一個新的數據庫連接,併爲其設置了隔離級別、是否只讀等屬性,

下面就是將事務信息記錄到當前線程中:

接下來就是記錄事務狀態並返回事務信息:

然後就是我們目標業務方法的執行了,根據執行結果的不同做提交或回滾操作,我們先看一下回滾操作:

紅框裏的方法,實際調用的是RuleBasedTransactionAttribute.rollbackOn()方法:

其中,rollbackRules就是我們@Transactional註解裏的rollbackFor屬性所配置的,

它是在tx自定義標籤解析的時候放進去的:

rule.getDepth(ex)方法邏輯如下:

super.rollbackOn(ex)即DefaultTransactionAttribute的rollbackOn方法:

接下來執行真正的回滾操作:

以上方法1,有保存點;

方法2:,是新事務;

方法3,加入到已有的事物

最後,清除事務信息:

保存點一般用於嵌入式事務,內嵌事務的回滾不會引起外部事務的回滾。下面我們來看新事務的回滾:

很簡單,就是獲取當前線程的數據庫連接並調用其rollback方法進行回滾,使用的是底層數據庫連接提供的API。

最後還有一個清理和恢復掛起事務的操作:

事務掛起操作:

如果事務執行前有事務掛起,那麼當前事務執行結束後需要將掛起的事務恢復,掛起事務時保存了原事務信息,

重置了當前事務信息,所以恢復操作就是將當前的事務信息設置爲之前保存的原事務信息。

到這裏事務的回滾操作就結束了,下面讓我們來看事務的提交操作:

在上文分析回滾流程中我們提到了如果當前事務不是獨立的事務,也沒有保存點,在回滾的時候只是設置一個回滾標記,

由外部事務提交時統一進行整體事務的回滾。如下:

最後執行processCommit方法:

提交操作也是很簡單的調用數據庫連接底層API的commit方法:

至此,Spring事務的源碼分析結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章