一、引言
在實際項目中,用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註解裏的幾個屬性定義的意圖:
- 指定了事務管理器
- 指定了隔離級別
- 指定事務的傳播性
- 指定哪些異常事務(不)作回滾
- 指定了事務超時時間
TransactionInterceptor
通過一個註解,spring是如何做到事務管理的呢?其實熟悉Spring的童鞋都很容易就想到,這得益於spring攔截器機制。沒錯,@Transactional註解事務管理的底層實現脈絡,就是使用攔截器。它就是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事務管理的奧妙之處還有很多,比如事務傳播性的實現、事務管理的委託等,還有很多其他實現細節將會在下一節作詳細展開剖析。
文章若有紕漏、邏輯不通的地方,還請大家批評指出!