1. Locate transaction invalidation at the source level
We know that Spring uses Spring AOP to implement the rollback of transactions, and Spring AOP uses dynamic proxies. At this point, we know that there are several conditions for transactions to be effective. The first is that there is a chain of enhancers that can be executed, which is similar to the various notifications we usually use. A default enhancer, TransactionInterceptor, is defined in Spring transactions. In this class @ EnableTransactionManagement annotation ProxyTransactionManagementConfiguration is registered. The second is that classes can be dynamically proxied. Let’s focus on specific classes to analyze the problem.
1.1 transaction classes cannot be proxied
Transaction classes can be created agency, the key is AbstractAutoProxyCreator postProcessAfterInitialization method of a class:
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean ! = null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) ! = bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }Copy the code
If the Bean does not go this far, the current Bean cannot be dynamically proxied. Usually the console prints a statement is not eligible for Getting Processed by all BeanPostProcessors. Not eligible for auto-proxying). Under this information by PostProcessorRegistrationDelegate BeanPostProcessorChecker postProcessAfterInitialization printed, why will print this? The notes are clear:
BeanPostProcessor that logs an info message when a bean is created during
BeanPostProcessor instantiation, i.e. when a bean is not eligible for
getting processed by all BeanPostProcessors.
Copy the code
BeanPostProcessor (BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor) The most common place is when Shiro configuration references normal beans ahead of time so that they cannot be proxied later, resulting in transaction invalidation.
1.2 the class does not have a qualified enhancer
We navigate to the transaction enhancer, the Invoke method of the TransactionInterceptor, and then to the specific processing details of the invokeWithinTransaction method of TransactionAspectSupport, This method is used to perform the rollback of the transaction. If debug finds that the method did not execute here, then the transaction is invalidated, most commonly by not adding transaction annotations to the method. Naturally, the current class does not meet the interception point for transaction notification, and the enhancer will not execute.
Here are some common reasons for transaction failure:
2. Reason of Spring transaction failure
2.1. Not Annotated Transactional
2.2 method classes are not added to the IOC container
The entire transaction environment is dependent on the IOC container and the classes are not handled.
2.3. Exceptions generated by methods are not RuntimeExceptions
We will gaze orientation to the following agent, TransactionAspectSupport completeTransactionAfterThrowing method
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
Copy the code
DefaultTransactionAttribute rollbackOn method:
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
Copy the code
2.4. Classes are preinitialized
If the console prints is not eligible for getting Processed by all BeanPostProcessors (for example: If you eligible for auto-proxying, watch out for this Bean because it cannot be dynamically proxied. The solution is to remove this Bean from some special classes, such as configuration classes.
3. Processing of Spring propagation behavior
The specific treatment in AbstractPlatformTransactionManager AbstractPlatformTransactionManager method.
4. Spring isolation level
We know that Spring delegates isolation levels to the underlying database, so in Spring the details are to set the isolation level, The concrete method in AbstractPlatformTransactionManager getTransaction doBegin (transaction, definition); Inside, you can find an implementation class to take a look.
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
Copy the code
The Transactional annotation rollbackFor
If configuration rollbackFor properties in the annotations, then walking to txInfo. The transactionAttribute. RollbackOn (ex) here, It will into RuleBasedTransactionAttribute rollbackOn method:
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; // If rollbackFor is configured, there is a rollback rule. if (this.rollbackRules ! = null) { for (RollbackRuleAttribute rule : {this.rollbackrules) {this.rollbackrules) {this.rollbackrules () {this.rollbackrules () {this.rollbackrules () {this.rollbackrules () {this.rollbackrules () {this.rollbackrules (); Int depth = rule-depth (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); } return ! (winner instanceof NoRollbackRuleAttribute); }Copy the code
So what happens if we configure it as Throwable?
// If the rule is Throwable, it will return a positive number on success. If no match is found, it will go to -1. 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); }Copy the code
Pre-knowledge complements Spring transaction principles