spring如何判断当前事务是否应该回滚
在没有看到源码的我理所应当的认为是判断异常类型是否相同这么简单。但是当我做了下面一个测试的时候发现和我想的完全不一样。
@Transactional(rollbackFor = BizException.class)
public void saveUser(User user){
this.userService.SaveUser(user);
int i = 1/0;
}
前面的代码1/0会抛出一个ArithmeticException是RuntimeException的子类,BizException是我们自定义的异常也是runtimeException的子类。结果我发现竟然被回滚了。。。
所以再来看下源码。
@Transactonal对应的advice是TransactionInterceptor,当我们调用方法的时候执行的是invokeWithinTransaction方法
try {
result = invocation.proceedWithInvocation();
} catch (Throwable var17) {
this.completeTransactionAfterThrowing(txInfo, var17);
throw var17;
} finally {
this.cleanupTransactionInfo(txInfo);
}
当发生异常的时候会调用completeTransactionAfterThrowing方法进行事务回滚的异常判断。
protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
}
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} catch (TransactionSystemException var6) {
this.logger.error("Application exception overridden by rollback exception", ex);
var6.initApplicationException(ex);
throw var6;
} catch (Error | RuntimeException var7) {
this.logger.error("Application exception overridden by rollback exception", ex);
throw var7;
}
} else {
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
} catch (TransactionSystemException var4) {
this.logger.error("Application exception overridden by commit exception", ex);
var4.initApplicationException(ex);
throw var4;
} catch (Error | RuntimeException var5) {
this.logger.error("Application exception overridden by commit exception", ex);
throw var5;
}
}
}
}
上面的逻辑代码是比较清晰的,判断当前异常类型我们回滚限定的类型,逻辑在txInfo.transactionAttribute.rollbackOn(ex))方法
public boolean rollbackOn(Throwable ex) {
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
//循环取出rule 就是我们的rollbackFor的Class<? extends Throwable>[]
for (RollbackRuleAttribute rule : this.rollbackRules) {
//这里为了取出层级,什么意思呢?如果当前抛出的异常是rollbackFor中的多个异常类的子类,
//取出当前最近的那个进行rollback,如果没有匹配的上的那么就是-1
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
//如果没有异常匹配的上的话就会调用父类的默认的rollbackon规则。
//父类的规则判断是继承与runtimeException或者是继承与Error。。。
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
return super.rollbackOn(ex);
}
//如果最终拿到的是不需要回滚的rule的话也是不回滚的
return !(winner instanceof NoRollbackRuleAttribute);
}
父类的判断是否回滚的判断
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
getDepth的代码如下:很简单的一个递归,不做过多的解释了。
public int getDepth(Throwable ex) {
return getDepth(ex.getClass(), 0);
}
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
上面的代码说实话对我来说有点颠覆。
1.首先异常的判断是会递归判断父级的。这个以前没有仔细研究过
2.然后如果最终没有匹配的上exception的话会最终判断当前抛出的异常是否是runtimeException的子类或者是Error的子类,然后最终判断是否回滚。