前のブログでは、トランザクションによって生成された動的プロキシオブジェクトの基になるコードを解析しました。簡単な要約は1文です。トランザクションアノテーション@Transactionalが追加され、メソッドがパブリックの場合、springはBeanのプロキシオブジェクトを生成します。 jdkかcglibかは、トランザクションメソッドに対応するクラスがインターフェースを実装しているかどうかによって異なります。
次に、プロキシオブジェクトが生成された後の実行プロセスを見てみましょう。
インターセプターの実行
org.springframework.transaction.interceptor.TransactionInterceptor
勉強中はcglibプロキシを使用していましたが、インターフェースを実装していなかったのでCGLIBプロキシを使用しました。ターゲットメソッドを呼び出すと呼び出されます。
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept; 然后会调用到
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
このメソッドの一部を切り取って、宣言型トランザクションとプログラム型トランザクションを区別します。ここでは、宣言型トランザクションのみに焦点を当てます。
@Nullable
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
//如果有异常就回滚事务,回滚时,根据配置的rollbackFor进行判断
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除事务信息
cleanupTransactionInfo(txInfo);
}
//提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
この方法では、
- トランザクションアノテーションに対応する伝播メカニズムに従って、新しいトランザクションを作成するか、現在のトランザクションにネストして実行するかなどを決定します。
- 次に、ターゲットメソッドを実行します
- ターゲットメソッドを実行した後、例外が発生しない場合は、トランザクションをコミットします
- 例外が発生した場合は、構成されたrollbackForおよびnoRollbackForに従ってロールバックするかどうかを決定します。ロールバックする必要がない場合はトランザクションを送信し、そうでない場合はトランザクションをロールバックします。
したがって、2つの方法に焦点を当てます
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
和
completeTransactionAfterThrowing(txInfo, ex);
伝送機構のソースコード
終了後、送信メカニズムのソースコードを追加します
例外ロールバックソースコード
ターゲットメソッドの実行中に例外が発生した場合、それをキャッチした後、処理のためにこのメソッドに入ります
/**
* 这里是判断是否需要回滚的逻辑
* 如果在事务注解上指定了回滚的异常类型、或者指定了不回滚的异常类型,就会在这里进行判断
* 1.如果判断满足回滚的条件,就事务回滚
* 先判断开发人员指定的类型,如果业务代码抛出的异常符合指定的类型,就回滚
* 如果没有指定,就判断是否是错误(error)或者运行时异常(runTimeException),如果是,就回滚
* 所以:如果程序员没有指定回滚的异常,默认情况下,如果是运行时异常或者是error(错误),也是会进行事务的回滚
* 2.否则,就提交事务
* @param txInfo
* @param ex
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
/**
* 如果程序员有指定回滚或者不回滚的异常,就会进入
* org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn(java.lang.Throwable)进行判断
*
* 如果没有指定,就默认调用org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(java.lang.Throwable)
* 进行判断
*/
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// 这里else就是不符合回滚的条件,会进行事务的提交(即使发生了异常场景)
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
ここでのより重要な方法の1つは、例外を判断し、現在のビジネスコードによってスローされた例外がロールバック要件を満たしているかどうかを判断することです。
ここでのこのメソッドは、現在スローされている例外が@Transactionalアノテーションで構成された例外ロールバック情報に準拠しているかどうかを判別するためのものです。
org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
@Override
public boolean rollbackOn(Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
// 这里的rollbackRules是在解析@Transactional注解的时候,保存的;parseTransactionAnnotation在这个方法中解析的注解;这里的rollbackRules既有rollbackException中配置的,也有noRollbackException中配置的
if (this.rollbackRules != null) {
for (RollbackRuleAttribute rule : this.rollbackRules) {
/**
* depth:当前rule和ex的相似度
*
* deepest:和ex最相近的depth
* winner:相似度最近的RollbackRuleAttribute
* 如果当前rule,返回的depth比上一次返回的depth小,且大于0,就用当前这次异常
*
* 这里会保存和业务代码抛出异常最相似的rule
* 比如:
* 我在代码中,抛出了一个java.lang.ArithmeticException: / by zero
*
* 如果我在@Transactional(rollbackFor = {Exception.class,ArithmeticException.class})
* 那肯定会返回ArithmeticException这个rule,因为ArithmeticException返回的depth是0
* Exception返回的的depth是2 下面解释为什么一个是0,一个是2
*/
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
// User superclass behavior (rollback on unchecked) if no rule matches.
// 如果没有匹配到最相似的异常、或者没有配置回滚异常类,就会执行这里,这里是调用父类的方法进行判断
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
return super.rollbackOn(ex);
}
/**
* 如果相似度最近的rule不是无需回滚的类型,就可以进行事务回滚
*/
return !(winner instanceof NoRollbackRuleAttribute);
}
// 这里就是父类对应的判断逻辑,也就是说:如果程序员单单加了一个@Transactional注解,那么在业务代码抛出运行时异常后者error的时候,还是会回滚的
@Override
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
次に、異常な類似性を判断する上記の方法を見てみましょう。intdepth = rule.getDepth(ex);
ここでは類似性と呼んでいますが、適切かどうかはわかりません。ビジネスコードでスローされた例外Aに対応する名前と、プログラマーが指定した例外Bに対応する名前を比較します。一致する場合は、一致する場合は深さを返します。一致しない場合は再帰的に呼び出し、ビジネスコードでスローされたA例外に対応する親クラスをB例外の名前と比較し、順番に再帰的に呼び出します。
/**
* 这里其实就是用业务代码抛出的异常和程序员在@Transactional注解中执行的异常信息进行对比
*
* 实际底层使用的是String.contains()方法
* exceptionName:就是程序员指定的异常对应的类名;比如:我指定的是rollbackFor = Exception.class,那这里的exceptionName就是:java.lang.Exception
*
* 1.如果当前抛出的异常和程序员指定的异常匹配不上,就依次递归调用抛出异常的父类和程序员指定的异常进行比较,
* 1.1 直到匹配上,就返回当前的depth,depth每递归调用一次,就+1
* 1.2 或者是到Throwable依旧没有比对上,这时,就表示我指定的异常和代码抛出的异常不匹配
*
* 这两种场景也好验证:
* 1.首先,我在业务代码中,加上这么一行代码:int i = 10/0;
* 2.然后在@Transactional注解中加上rollbackException = Exception.class 或者是rollbackException = IoException.class
* 这两种异常,最后事务都会回滚,但是效果却是不一样的
* 如果我加的是rollbackException = Exception.class,这里会匹配上,返回的是depth是2
* 但是如果加的是rollbackException = IoException.class,这里返回的是-1
*
* 因为:如果是rollbackException = Exception.class;那这里在匹配的时候,会递归调用两次,
* int i = 10/0;会抛出java.lang.ArithmeticException: / by zero
* ArithmeticException的父类是RuntimeException;RuntimeException的父类是Exception;所以只有递归调用两次,才能匹配到我指定的Exception.class
*
* 但是,如果我指定的是IoException.class,那永远也匹配不上,因为IOException和ArithmeticException都继承了RuntimeException,是并行的关系,在最后
* 递归调用到父类Throwable的时候,就会返回-1(即使这里返回了-1,最后事务还是会回滚,为什么?因为在方法之后会判断,如果程序员指定的异常和当前业务代码抛出的异常不相似,那就会判断业务代码抛出的异常是否是运行时异常或者error)
*
* @param exceptionClass:当前业务代码抛出的异常/或者是抛出异常对应的父类
* @param depth:相似度/或者说是深度
* @return
*/
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);
}
したがって、ビジネスコードが例外をスローすると、
- プログラマーが指定した例外タイプと例外タイプを判断し、一致度が最も類似している例外を見つけ、最後に、最も類似している例外がnoRollbackExceptionによって構成された例外クラスであるかどうかを判断します。ロールバックしてトランザクションを送信します。
- 一致するものがない場合は、スローされた例外がErrorであるかRuntimeExceptionであるかを判断します。一致する場合は、ロールバックされます。