一、引言
在实际项目中,用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事务管理的奥妙之处还有很多,比如事务传播性的实现、事务管理的委托等,还有很多其他实现细节将会在下一节作详细展开剖析。
文章若有纰漏、逻辑不通的地方,还请大家批评指出!