@Transactional註解實現事務管理的原理

一、引言

在實際項目中,用Spring進行事務控制,我們通常都用@Transactional註解。這個註解用法很簡單,把原來jdbc繁瑣的事務控制都濃縮在這個註解的使用上了。秉着“知其然,知其所以然”的心態,我們可以思考,這個註解那麼牛掰,spring是如何實現的呢?這一切都得益於Spring那套強大的生態系統。

二、最簡版事務管理

先來看看最簡版的jdbc事務處理:

//獲取連接
Connection conn = getConnnection();
Statement stat = conn.createStatement();
//設置爲手動提交事務
conn.setAutoCommit(false);

//開啓事務
conn.beginTX();

try{
	//執行CRUD操作
	stat.executeCRUD(String sql);
	stat.executeCRUD2(String sql);
	//省略一系列代碼邏輯
	//...
}catch(Exception e){
	//事務回滾
	conn.rollBack();
	throw e;
}

//提交事務
conn.commitTX();

三、Transactional註解實現原理

所謂萬變不離其宗,註解方式的spring事務管理實現看似複雜,其實也就是基於最簡版的jdbc事務管理,進行封裝、延申和擴展,讓開發者使用更方便、快捷。只要我們弄懂其根本的原理,再結合spring的一些特性和優勢,spring事務管理問題也迎刃而解。

Transactional註解簡述

先來看看Transactional註解裏的幾個屬性元素

public @interface Transactional {

    @AliasFor("transactionManager")
	String value() default "";//事務管理器別名
	@AliasFor("value")
	String transactionManager() default "";//事務管理器
	//傳播性
	Propagation propagation() default Propagation.REQUIRED;
	//隔離級別
	Isolation isolation() default Isolation.DEFAULT;
	//超時時間
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
	//是否只讀
	boolean readOnly() default false;
	//指定回滾異常
	Class<? extends Throwable>[] rollbackFor() default {};
	//指定回滾異常
	String[] rollbackForClassName() default {};
	//指定不回滾異常
	Class<? extends Throwable>[] noRollbackFor() default {};
	//指定不回滾異常
	String[] noRollbackForClassName() default {};
}

可以總結一下Transactional註解裏的幾個屬性定義的意圖:

  1. 指定了事務管理器
  2. 指定了隔離級別
  3. 指定事務的傳播性
  4. 指定哪些異常事務(不)作回滾
  5. 指定了事務超時時間

TransactionInterceptor

通過一個註解,spring是如何做到事務管理的呢?其實熟悉Spring的童鞋都很容易就想到,這得益於spring攔截器機制。沒錯,@Transactional註解事務管理的底層實現脈絡,就是使用攔截器。它就是TransactionInterceptor。
TransactionInterceptor
TransactionInterceptor是繼承於TransactionAspectSupport的攔截器,攔截器的原理在這裏就不細說了。被@Transactional註解的方法會被TransactionInterceptor攔截,其invoke方法被調用,最終會調用父類TransactionAspectSupport的invokeWithinTransaction方法。

	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

TransactionAspectSupport

TransactionAspectSupport的最重要方法是invokeWithinTransaction方法,以下代碼塊是源碼中截取的,已經省略了有回調功能的CallbackPreferringPlatformTransactionManager實現邏輯,這裏只闡述非回調的事務管理。

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

		// If the transaction attribute is null, the method is non-transactional.
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		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 = null;
			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);
			return retVal;
		}
         //省略下面CallbackPreferringPlatformTransactionManager的實現邏輯
         //......
	}

從invokeWithinTransaction方法的源碼中,我們可以看到熟悉的影子。completeTransactionAfterThrowing方法對應於jdbc事務管理中的conn.rollBack()方法,而commitTransactionAfterReturning則對應conn.commitTX()方法,那麼獲取連接、開啓事務在哪裏呢?其實就在createTransactionIfNecessary方法裏,這也是個很重要的方法,spring事務管理的精髓就在其中,至於這個方法的細節,考慮到篇幅問題,將會在下一節講到。
我們還注意到invokeWithinTransaction方法裏有個invocation.proceedWithInvocation()方法的調用,可以看看源碼中的描述:

// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.

可以知道這個方法的作用是調用攔截器鏈中的下一個攔截器(涉及到spring攔截器的知識不再在這裏贅述),最終返回的是被@Transactional註解的方法的返回值。這個方法被try-catch包圍了,其實這方法可以類比於jdbc事務管理中的執行的一系列CRUD方法。

四、總結

本文拿jdbc事務管理跟spring實現的事務管理進行了一個對比,其實spring事務管理就是基於jdbc事務管理之上,做了一些增強,在這裏不得不感嘆spring的強大!spring事務管理的奧妙之處還有很多,比如事務傳播性的實現、事務管理的委託等,還有很多其他實現細節將會在下一節作詳細展開剖析。

文章若有紕漏、邏輯不通的地方,還請大家批評指出!

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