在上一篇Spring源码解读:@Transactional注解是如何生效的里面,我们讲解了咱们这个AOP 的advisor(BeanFactoryTransactionAttributeSourceAdvisor)中的pointCut(TransactionAttributeSourcePointcut)的逻辑,主要靠matches方法来判断advisor是否需要生效。那么今天就来分析下BeanFactoryTransactionAttributeSourceAdvisor中的advice(TransactionInterceptor)的具体执行逻辑。
可以看到TransactionInterceptor确实是一个Advice,但是其实Advice和Interceptor这两个接口类,并没有方法需要实现,更像是标志类,或者说是为了实现动态而设计的类。
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
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实现一个简单的频控拦截器中的拦截方法实现逻辑。
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方法
结合上面的类图结构可以看到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进行事务提交。
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
// 获取一个数据库事务对象(DataSourceTransactionObject),
// 这个对象中封装了一个从当前线程上下文中获取到的连接
Object transaction = doGetTransaction();
此处就不详细讲了,doGetTransaction的逻辑就是注释上讲的,利用一个ThreadLocal字段resources保存事务,key是dataSource信息,value是ConnectHolder,因为项目可以配置多数据源,所以ThreadLocal里的泛型是map
// 判断是否存在事务(也就是连接)
// 如果之前获取到的连接不为空,并且连接上激活了事务,那么就为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
判断当前事务方法设置的事务传播级别
1. 如果事务传播级别是PROPAGATION_MANDATORY,直接报错,因为该类型,必须要被外围事务方法调用嵌套
2. 如果事务传播级别是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED 挂起一个空事务(此处存疑,不知何用);和上面的PROPAGATION_REQUIRES_NEW,做的事儿相同,开始新事务,并设置resources值,返回TransactionStatus
3. 其他的事务传播级别,返回一个当前事务为空,挂起事务为空的TransactionStatus
把3.1.1步骤返回的TransactionStatus和一些其他事务基本信息,如@Transactional注解信息、PlatformTransactionManager一起封装成一个TransactionInfo对象返回
retVal = invocation.proceedWithInvocation();
此处的invocation.proceedWithInvocation,其实执行的是invoke调用时传入的invocation::proceed,也确实是咱们的method的逻辑
// 方法执行出现异常,在异常情况下完成事务,可能回滚
// 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的逻辑,其实主体逻辑就是恢复被挂起的事务
@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方法