Spring源码解读:@Transactional原理(2)

文章目录

  • 前言
  • 一、类图
  • 二、类关系
    • 1. TransactionStatus
  • 三、MethodInterceptor#invoke
    • 1.方法拦截器?
    • 2.TransactionInterceptor#invoke
    • 3. TransactionAspectSupport#invokeWithinTransaction
      • 3.1 createTransactionIfNecessary
        • 3.1.1 AbstractPlatformTransactionManager#getTransaction,获取事务状态TransactionStatus
          • 3.1.1.1 调用当前事务方法前,已有事务
          • 3.1.1.2 调用本事务方法前,没有事务
        • 3.1.2 prepareTransactionInfo
      • 3.2 真正的业务运行
      • 3.3 执行遇到了异常
      • 3.4 正常执行结束,进行commit操作
  • 总结


前言

在上一篇Spring源码解读:@Transactional注解是如何生效的里面,我们讲解了咱们这个AOP 的advisor(BeanFactoryTransactionAttributeSourceAdvisor)中的pointCut(TransactionAttributeSourcePointcut)的逻辑,主要靠matches方法来判断advisor是否需要生效。那么今天就来分析下BeanFactoryTransactionAttributeSourceAdvisor中的advice(TransactionInterceptor)的具体执行逻辑。

一、类图

Spring源码解读:@Transactional原理(2)_第1张图片

可以看到TransactionInterceptor确实是一个Advice,但是其实Advice和Interceptor这两个接口类,并没有方法需要实现,更像是标志类,或者说是为了实现动态而设计的类。

二、类关系

1. TransactionStatus

public class DefaultTransactionStatus extends AbstractTransactionStatus {

	@Nullable
	private final Object transaction;

	private final boolean newTransaction;

	private final boolean newSynchronization;

	private final boolean readOnly;

	private final boolean debug;

	@Nullable
	private final Object suspendedResources;
}

其中最重要的就是newTransaction和newSynchronization。分别表示是否是新事务和是否是是否是第一次同步操作。
具体作用:newTransaction的作用比较简单,就是标志当前使用的事务是否是新的事务,具体用在判断是否需要commit;而newSynchronization是用来控制TransactionSynchronizationManager类的init和clear的,只有为true才会进行,同时用来控制钩子函数是否执行。具体有如下的几个钩子函数(AbstractPlatformTransactionManager):

钩子函数1:triggerBeforeCommit
钩子函数2:triggerBeforeCompletion
钩子函数3:triggerAfterCommit
钩子函数4:triggerAfterCompletion

其实正常能够commit的时候执行的时候,钩子函数1和钩子函数2执行只有先后顺序,钩子函数3和钩子函数4执行也只有先后顺序,中间没有插入其他关于事务的逻辑。

newTransaction取值为true或者false的时机好理解
newSynchronization在同一线程内只有第一次调用newTransaction为true的时候,才为true,其余时候都是false

也就是每个加了@Transactional注解的方法,都会对应生成TransactionStatus对象,但是在同一个线程内只有第一个TransactionStatus对象的newSynchronization才为true

三、MethodInterceptor#invoke

1.方法拦截器?

public interface MethodInterceptor extends Interceptor {

	/**
	 * Implement this method to perform extra treatments before and
	 * after the invocation. Polite implementations would certainly
	 * like to invoke {@link Joinpoint#proceed()}.
	 * @param invocation the method invocation joinpoint
	 * @return the result of the call to {@link Joinpoint#proceed()};
	 * might be intercepted by the interceptor
	 * @throws Throwable if the interceptors or the target object
	 * throws an exception
	 */
	Object invoke(MethodInvocation invocation) throws Throwable;
}

按照invoke上面这方法的说明,就是该实现应该写成Joinpoint#proceed()的invoke的方法,也就是在joinpoint.proceed()逻辑的前后加上自己的逻辑。例如在基于AOP+Redis实现一个简单的频控拦截器中的拦截方法实现逻辑。

2.TransactionInterceptor#invoke

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);
	}

可以看到主要实现的是invokeWithTransaction方法

3. TransactionAspectSupport#invokeWithinTransaction

结合上面的类图结构可以看到TransactionInterceptor继承了TransactionAspectSupport,所以TransactionInterceptor#invoke的实际逻辑走的是TransactionAspectSupport#invokeWithinTransaction
咱们只挑重点代码分析。

			// 创建事务/获取事务;事务信息txInfo可能为空
			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
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				// 清除线程中的事务信息
				cleanupTransactionInfo(txInfo);
			}
			// 提交事务
			commitTransactionAfterReturning(txInfo);
			return retVal;

上面是主体逻辑,总结一下就是获取事务信息,执行业务逻辑,如果有异常就调用completeTransactionAfterThrowing进行回滚,否则最终执行commitTransactionAfterReturning进行事务提交。

3.1 createTransactionIfNecessary

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				// 调用事务管理器的方法,获取一个事务并返回事务的状态
				// 事务管理器根据@Transactional的注解信息返回事务状态
				// important!!!
				status = tm.getTransaction(txAttr);
			}
			else {
				// do something
			}
		}
		// 将事务相关信息封装到TransactionInfo对象中
		// 并将TransactionInfo绑定到当前线程,放到ThreadLocal中
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

首先需要获得TransactionStatus对象,咱们需要有这样的理解,一个事务,也就是数据库的某个connection一次处理的业务逻辑,也对应于mysql的事务,mysql的事务默认是自动提交的,spring在获取连接的时候将自动提交关闭,所以可以在一次connection里进行多个数据库操作,都执行成功,最终手动commit

3.1.1 AbstractPlatformTransactionManager#getTransaction,获取事务状态TransactionStatus
		// 获取一个数据库事务对象(DataSourceTransactionObject),
		// 这个对象中封装了一个从当前线程上下文中获取到的连接
		Object transaction = doGetTransaction();

此处就不详细讲了,doGetTransaction的逻辑就是注释上讲的,利用一个ThreadLocal字段resources保存事务,key是dataSource信息,value是ConnectHolder,因为项目可以配置多数据源,所以ThreadLocal里的泛型是map

3.1.1.1 调用当前事务方法前,已有事务
		// 判断是否存在事务(也就是连接)
		// 如果之前获取到的连接不为空,并且连接上激活了事务,那么就为true
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			// 如果已经存在了事务,需要根据不同传播机制进行不同的处理
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

在讲handleExistingTransaction处理逻辑之前,我们先讲解下挂起事务suspend方法的逻辑:

1.把当前事务对象transaction里的connectionHolder置为null,
2.把resources里对应DataSource的value给remove掉
3.把TransactionSynchronizationManager里标志的当前事务的一些属性给置空或者置为false
4.返回被挂起的事务

handleExistingTransaction的逻辑描述一下:
判读当前事务方法设置的事务传播级别

1.如果事务的传播机制是PROPAGATION_NEVER,直接报错
2.如果事务的传播机制是PROPAGATION_NOT_SUPPORTED,就挂起当前事务。返回一个当前事务为空,保存了挂起事务的TransactionStatus
3. 如果事务的传播机制是PROPAGATION_REQUIRES_NEW,挂起当前事务。基于当前事务信息,设置当前事务信息,跟suspend做的事情相反,并开启事务,返回包含了新事务以及挂起事务的TransactionStatus
4. 如果事务的传播机制是PROPAGATION_NESTED,不挂起当前事务,会给当前事务创建一个保存点(save point),如果后续发生异常回滚,只会回滚到该保存点。开启事务,返回包含了当前事务的TransactionStatus
5. 剩余事务类型(supports、required、mandatory)需要考虑是否要进行隔离级别判断:如果需要判断,就要确保当前方法传入的@Transactional注解里设置的隔离级别和当前事务的隔离级别是一致的,如果不一致会报错。
6. 剩余事务类型(supports、required、mandatory):不挂起事务,返回包含了当前事务的TransactionStatus
3.1.1.2 调用本事务方法前,没有事务

判断当前事务方法设置的事务传播级别

1. 如果事务传播级别是PROPAGATION_MANDATORY,直接报错,因为该类型,必须要被外围事务方法调用嵌套
2. 如果事务传播级别是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED 挂起一个空事务(此处存疑,不知何用);和上面的PROPAGATION_REQUIRES_NEW,做的事儿相同,开始新事务,并设置resources值,返回TransactionStatus
3. 其他的事务传播级别,返回一个当前事务为空,挂起事务为空的TransactionStatus
3.1.2 prepareTransactionInfo

把3.1.1步骤返回的TransactionStatus和一些其他事务基本信息,如@Transactional注解信息、PlatformTransactionManager一起封装成一个TransactionInfo对象返回

3.2 真正的业务运行

retVal = invocation.proceedWithInvocation();

此处的invocation.proceedWithInvocation,其实执行的是invoke调用时传入的invocation::proceed,也确实是咱们的method的逻辑

3.3 执行遇到了异常

		// 方法执行出现异常,在异常情况下完成事务,可能回滚
		// target invocation exception
		completeTransactionAfterThrowing(txInfo, ex);
		throw ex;

看看具体逻辑

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				// do something
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				// 走到此处逻辑,应该不是因为transactionAttribute,而是因为发生的异常不是rollBack设置的异常类型,所以需要执行commit操作
				try {
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				// do something
			}
		}
	}

看看回滚操作具体逻辑

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;
			try {
				triggerBeforeCompletion(status);// 这是个扩展点,可由不同的TransactionManager各自实现,咱们用的DataSourceTransactionManager没有自定义实现,父类的实现为空
				if (status.hasSavepoint()) {
				//此处便对应了PROPAGATION_NESTED事务传播级别
					status.rollbackToHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					// 底层其实就是调用的数据库连接connection.rollBack
					doRollback(status);
				}
				else {
					// Participating in larger transaction
					// 执行到此处的,要么是嵌套的NOT_SUPPORTED的事务传播级别,要么是supports、required、mandatory其中一个事务传播级别,由于isGlobalRollbackOnParticipationFailure()默认是true,所以需要设置rollBackOnly字段,而不需要回滚。
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							doSetRollbackOnly(status);
						}
						// do something
					}
				}
			}
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
		}
		finally {
			// 此处很重要!!!恢复被挂起的事务
			cleanupAfterCompletion(status);
		}
	}

来看看回滚之后进行的操作,也就是cleanupAfterCompletion的逻辑,其实主体逻辑就是恢复被挂起的事务

3.4 正常执行结束,进行commit操作

@Override
	public final void commit(TransactionStatus status) throws TransactionException {
	// do something
		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		// 如果因为抛出异常设置过rollbackOnly,此处要继续调用processRollBack,其实应该不会有新逻辑处理
		if (defStatus.isLocalRollbackOnly()) {
			processRollback(defStatus, false);
			return;
		}
		if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			processRollback(defStatus, true);
			return;
		}
		processCommit(defStatus);
	}

看看commit的具体逻辑

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;
			try {
				boolean unexpectedRollback = false;
				prepareForCommit(status);
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;

				// 此处便是为了事务的NESTED传播机制进行的判断
				if (status.hasSavepoint()) {
				// 如果设置了保存点,那么commit也只commit到此处
					unexpectedRollback = status.isGlobalRollbackOnly();
					status.releaseHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
				// 如果是非嵌套的事务,也就是最外层或者REQUIRES_NEW得到的事务,那么进行提交
				// unexpectedRollback其实主要判断的还是rollBackOnly
					unexpectedRollback = status.isGlobalRollbackOnly();
					// 其实底层调用的是数据库连接connect.commit();
					doCommit(status);
				}
				else if (isFailEarlyOnGlobalRollbackOnly()) {
					unexpectedRollback = status.isGlobalRollbackOnly();
				}
				// Throw UnexpectedRollbackException if we have a global rollback-only
				// marker but still didn't get a corresponding exception from commit.
				if (unexpectedRollback) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			// do something
			// Trigger afterCommit callbacks, with an exception thrown there
			// propagated to callers but the transaction still considered as committed.
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
			//恢复挂起事务
			cleanupAfterCompletion(status);
		}
	}

总结

主要分析了下@Transactional注解真实的invoke逻辑,也就是BeanFactoryTransactionAttributeSourceAdvisor的Advice =》 TransactionInterceptor#invoke方法

你可能感兴趣的:(AOP,spring,源码分析,spring,java,aop,Transactional)