Bootstrap

@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事务管理的奥妙之处还有很多,比如事务传播性的实现、事务管理的委托等,还有很多其他实现细节将会在下一节作详细展开剖析。

文章若有纰漏、逻辑不通的地方,还请大家批评指出!

;