|
||
|
catalogue
text Series catalogDetailed explanation of spring transactions (I) preliminary exploration Detailed explanation of spring transaction (II) simple example spring transaction explanation (III) source code explanation Detailed explanation of spring transactions (IV) test and verification Detailed explanation of spring Affairs (V) summary and improvement 1, IntroductionIn Spring, transactions can be implemented in two ways:
Declarative transaction management does not need intrusion code. Transaction operations can be carried out through @ Transactional, which is faster and simpler (especially with spring boot automatic configuration, it can be said to be extremely streamlined!), And most businesses can be met. It is recommended to use. In fact, whether it is a programmatic transaction or a declarative transaction, the underlying core code of the final call is the same. This chapter starts with programming and declaration, and then enters the penetrating explanation of the core source code. 2, Transaction source code2.1 programmatic transaction templateFor programming transactions, Spring has provided us with the template class TransactionTemplate, which can be used conveniently, as shown in the following figure: The full pathname of the transactiontemplate is: org springframework. transaction. support. TransactionTemplate. Look at the package name. This is the Template class of spring for transactions. (spring is all kinds of templates...), Look at the class diagram below: At first glance, yo Xi has implemented two interfaces: TransactionOperations and InitializingBean (those familiar with spring source code know that this InitializingBean is an old routine). Let's look at the interface source code as follows:
As shown in the figure above, the TransactionOperations interface is used to execute the callback method of transactions. InitializingBean is a typical spring bean initialization process (air ticket: Spring IOC (IV) summary and sublimation )The reserved interface of is specially used to execute the method when the bean properties are loaded. Back to the point, what does the impl method of the two interfaces of TransactionTemplate do?
As shown in the above figure, in fact, afterpropertieset only verifies that the transaction manager is not empty, and execute() is the core method. The main steps of execute are as follows: 1.getTransaction() gets the transaction. See 3.3.1 for the source code 2.doInTransaction() executes business logic. Here is the user-defined business code. If there is no return value, it is doInTransactionWithoutResult(). 3.commit() transaction commit: call the commit of AbstractPlatformTransactionManager, rollback onexception() exception rollback: call the rollback() of AbstractPlatformTransactionManager, transaction commit rollback, see 3.3.3 for the source code
2.2 declarative transaction @ Transactional1.AOP related conceptsDeclarative transactions use spring AOP, which is aspect oriented programming. (what ❓ You don't know what AOP is In a word, make the repeated code in the business code into a facet, extract it, and define which methods need to execute this facet. Other self Baidu Bar...) The core concepts of AOP are as follows:
2. Declarative affairsThe declarative transaction calls the process as a whole, and two lines can be drawn: 1. Use proxy mode to generate proxy enhancement classes. 2. According to the agent transaction management configuration class, configure the weaving of transactions, enhance the surrounding before and after the business method, and add some transaction related operations. For example, get transaction properties, commit transactions, and roll back transactions. The process is as follows: Declarative transactions use @ Transactional annotation, so we load them from the automatic configuration when the spring boot container starts( spring boot container startup details )Start watching. At / meta-inf / spring Find in the configuration file in factories, as shown in the following figure:
Load 2 automatic configuration classes about transactions: org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, 1 @Configuration 2 @ConditionalOnClass(PlatformTransactionManager.class) 3 @AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, 4 DataSourceTransactionManagerAutoConfiguration.class, 5 Neo4jDataAutoConfiguration.class }) 6 @EnableConfigurationProperties(TransactionProperties.class) 7 public class TransactionAutoConfiguration { 8 9 @Bean 10 @ConditionalOnMissingBean 11 public TransactionManagerCustomizers platformTransactionManagerCustomizers( 12 ObjectProvider<List<PlatformTransactionManagerCustomizer<?>>> customizers) { 13 return new TransactionManagerCustomizers(customizers.getIfAvailable()); 14 } 15 16 @Configuration 17 @ConditionalOnSingleCandidate(PlatformTransactionManager.class) 18 public static class TransactionTemplateConfiguration { 19 20 private final PlatformTransactionManager transactionManager; 21 22 public TransactionTemplateConfiguration( 23 PlatformTransactionManager transactionManager) { 24 this.transactionManager = transactionManager; 25 } 26 27 @Bean 28 @ConditionalOnMissingBean 29 public TransactionTemplate transactionTemplate() { 30 return new TransactionTemplate(this.transactionManager); 31 } 32 } 33 34 @Configuration 35 @ConditionalOnBean(PlatformTransactionManager.class) 36 @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) 37 public static class EnableTransactionManagementConfiguration { 38 39 @Configuration 40 @EnableTransactionManagement(proxyTargetClass = false) 41 @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) 42 public static class JdkDynamicAutoProxyConfiguration { 43 44 } 45 46 @Configuration 47 @EnableTransactionManagement(proxyTargetClass = true) 48 @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) 49 public static class CglibAutoProxyConfiguration { 50 51 } 52 53 } 54 55 } TransactionAutoConfiguration mainly focuses on: 1.2 class annotations @ConditionalOnClass(PlatformTransactionManager.class) means that the automatic configuration takes effect when the PlatformTransactionManager class is included in the class path. This class is the core package of spring transactions and must be introduced. @Autoconfigurareafter ({jtaautoconfiguration. Class, hibernatejpaautoconfiguration. Class, datasourcetransactionmanagerautoconfiguration. Class, neo4jdataautoconfiguration. Class}), this configuration takes effect only after the four configuration classes in brackets. 2. 2 internal classes TransactionTemplateConfiguration transaction template configuration class: @ConditionalOnSingleCandidate(PlatformTransactionManager.class) takes effect only when a PlatformTransactionManager bean can be uniquely identified. @ConditionalOnMissingBean if no TransactionTemplate bean is defined, generate one. EnableTransactionManagementConfiguration enable transaction manager configuration class: @ConditionalOnBean(PlatformTransactionManager.class) takes effect when there is a PlatformTransactionManager bean. @ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) takes effect only when there is no custom Abstract transaction manager configuration class. (that is, the user-defined Abstract transaction manager configuration class will take precedence. If not, use this default transaction manager configuration class) EnableTransactionManagementConfiguration supports two proxy methods:
@EnableTransactionManagement(proxyTargetClass = false), that is, proxyTargetClass = false indicates that it is a JDK dynamic agent. It supports interface oriented agent. @Conditionalonproperty (prefix = "spring. AOP", name = "proxy target class", havingvalue = "false", matchifmissing = false), i.e. spring aop. It will take effect when proxy target class = false, and it will not take effect without this configuration.
@EnableTransactionManagement(proxyTargetClass = true), that is, proxyTargetClass = true identifies that Cglib agent supports subclass inheritance agent. Note that there is no configuration by default and the Cglib agent is used. Note: the @ Transactional annotation supports adding directly to the class. Well, after reading so many configuration classes, I finally came to the annotation @ EnableTransactionManagement. 1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(TransactionManagementConfigurationSelector.class) 5 public @interface EnableTransactionManagement { 6 7 //proxyTargetClass = false Say yes JDK Dynamic proxy supports interface proxy. true Say yes Cglib Proxy supports subclass inheritance proxy. 8 boolean proxyTargetClass() default false; 9 10 //Transaction notification mode (aspect weaving mode), default proxy mode (interceptors calling each other in the same class will not take effect), and enhanced AspectJ can be selected 11 AdviceMode mode() default AdviceMode.PROXY; 12 13 //When there are multiple notifications on the connection point, they are sorted. The default is the lowest. The higher the value, the lower the priority. 14 int order() default Ordered.LOWEST_PRECEDENCE; 15 16 } Focus on the class annotation @ Import(TransactionManagementConfigurationSelector.class) The class diagram of TransactionManagementConfigurationSelector is as follows: As shown in the figure above, the TransactionManagementConfigurationSelector inherits from the AdviceModeImportSelector and implements the ImportSelector interface. 1 public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { 2 3 /** 4 * {@inheritDoc} 5 * @return {@link ProxyTransactionManagementConfiguration} or 6 * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and 7 * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively 8 */ 9 @Override 10 protected String[] selectImports(AdviceMode adviceMode) { 11 switch (adviceMode) { 12 case PROXY: 13 return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; 14 case ASPECTJ: 15 return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; 16 default: 17 return null; 18 } 19 } 20 21 } As shown in the figure above, the selectImports method will eventually be executed to import the classes to be loaded. We only see that in the proxy mode, two classes, autoproxyregister and proxytransactionmanagementconfigurationare loaded.
AutoProxyRegistrarFirst, autoproxyregister implements the importbeandefinitionregister interface and replicates the registerBeanDefinitions method. The source code is as follows: 1 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 2 boolean candidateFound = false; 3 Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); 4 for (String annoType : annoTypes) { 5 AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); 6 if (candidate == null) { 7 continue; 8 } 9 Object mode = candidate.get("mode"); 10 Object proxyTargetClass = candidate.get("proxyTargetClass"); 11 if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && 12 Boolean.class == proxyTargetClass.getClass()) { 13 candidateFound = true; 14 if (mode == AdviceMode.PROXY) {//proxy pattern 15 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); 16 if ((Boolean) proxyTargetClass) {//If it is CGLOB Subclass proxy mode 17 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); 18 return; 19 } 20 } 21 } 22 } 23 if (!candidateFound) { 24 String name = getClass().getSimpleName(); 25 logger.warn(String.format("%s was imported but no annotations were found " + 26 "having both 'mode' and 'proxyTargetClass' attributes of type " + 27 "AdviceMode and boolean respectively. This means that auto proxy " + 28 "creator registration and configuration may not have occurred as " + 29 "intended, and components may not be proxied as expected. Check to " + 30 "ensure that %s has been @Import'ed on the same class where these " + 31 "annotations are declared; otherwise remove the import of %s " + 32 "altogether.", name, name, name)); 33 } 34 } Agent mode: aopconfigutils registerAutoProxyCreatorIfNecessary(registry); The final call is: registerorexcalateapcasrequired (infrastructure advisor autoproxycreator. Class, registry, source); Infrastructure enhanced automatic proxy constructor 1 private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { 2 Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); As shown in the figure above, APC_ PRIORITY_ The list is as follows: 1 /** 2 * Stores the auto proxy creator classes in escalation order. 3 */ 4 private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>(); 5 6 /** 7 * Priority up list 8 */ 9 static { 10 APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); 11 APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); 12 APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); 13 } As shown in the figure above, because the first index of the class infrastructureasuggestorautoproxycreator in the list is 0, the requiredpriority is the smallest, and it does not enter, so the beanClassName is not reset, nothing is done, and null is returned When will the enhanced proxy class be generated?The class diagram of infrastructure advisor autoproxycreator is as follows: As shown in the figure above, look at the two core methods: postprocessbeforeinstance of the InstantiationAwareBeanPostProcessor interface + postProcessAfterInitialization of the BeanPostProcessor interface. About spring bean life cycle airline ticket: Spring IOC (IV) summary and sublimation 1 @Override 2 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { 3 Object cacheKey = getCacheKey(beanClass, beanName); 4 5 if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { 6 if (this.advisedBeans.containsKey(cacheKey)) {//If it already exists, return directly 7 return null; 8 }//Whether basic components (basic construction does not require agents): Advice, Pointcut, Advisor and AopInfrastructureBean are all basic construction 9 if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { 10 this.advisedBeans.put(cacheKey, Boolean.FALSE);//Add intoadvisedBeans ConcurrentHashMap<k=Object,v=Boolean>Mark whether the implementation needs to be enhanced. Here is the basic construction bean No agent is required, all are set to false,For the backpostProcessAfterInitializationUse after instantiation. 11 return null; 12 } 13 } 14 15 // TargetSource yes spring aop The user-defined instantiated interface is reserved for us. If it existsTargetSource It will not be instantiated by default, but in a user-defined way. We have no definition and do not enter 18 if (beanName != null) { 19 TargetSource targetSource = getCustomTargetSource(beanClass, beanName); 20 if (targetSource != null) { 21 this.targetSourcedBeans.add(beanName); 22 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); 23 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); 24 this.proxyTypes.put(cacheKey, proxy.getClass()); 25 return proxy; 26 } 27 } 28 29 return null; 30 } Through tracing, since infrastructure advisor autoproxycreator is an infrastructure class, advisedBeans.put(cacheKey, Boolean.FALSE) Add the adviedbeans concurrenthashmap < k = object, v = Boolean > flag to indicate whether the implementation needs to be enhanced. Here, the basic build beans do not need an agent and are set to false for later use after the instantiation of postProcessAfterInitialization. Let's look at the source code of postProcessAfterInitialization as follows: 1 @Override 2 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 3 if (bean != null) { 4 Object cacheKey = getCacheKey(bean.getClass(), beanName); 5 if (!this.earlyProxyReferences.contains(cacheKey)) { 6 return wrapIfNecessary(bean, beanName, cacheKey); 7 } 8 } 9 return bean; 10 } 11 12 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { The core method createProxy is as follows: 1 protected Object createProxy( 2 Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { 3 // If it isConfigurableListableBeanFactoryInterface (US) DefaultListableBeanFactory Is the implementation class of the interface), then expose the target class 4 if (this.beanFactory instanceof ConfigurableListableBeanFactory) { Finally, we generate the CGLIB proxy class So far, we have analyzed the construction process of proxy class. ProxyTransactionManagementConfigurationLet's look at ProxyTransactionManagementConfiguration: 1 @Configuration 2 public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { 3 4 @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) 5 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)//Define transaction enhancer 6 public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { 7 BeanFactoryTransactionAttributeSourceAdvisor j = new BeanFactoryTransactionAttributeSourceAdvisor(); 8 advisor.setTransactionAttributeSource(transactionAttributeSource()); 9 advisor.setAdvice(transactionInterceptor()); 10 advisor.setOrder(this.enableTx.<Integer>getNumber("order")); 11 return advisor; 12 } 13 14 @Bean 15 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)//Define annotation based transaction attribute resources 16 public TransactionAttributeSource transactionAttributeSource() { 17 return new AnnotationTransactionAttributeSource(); 18 } 19 20 @Bean 21 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)//Define transaction interceptors 22 public TransactionInterceptor transactionInterceptor() { 23 TransactionInterceptor interceptor = new TransactionInterceptor(); 24 interceptor.setTransactionAttributeSource(transactionAttributeSource()); 25 if (this.txManager != null) { 26 interceptor.setTransactionManager(this.txManager); 27 } 28 return interceptor; 29 } 30 31 } Core method: transactionAdvisor() transaction weaving An advisor is defined to set transaction properties, transaction interceptor and order. The core is the transaction interceptor. TransactionInterceptor uses the common spring transaction infrastructure to implement "declarative transactions", inherits from the TransactionAspectSupport class (which contains integration with spring's underlying transaction API) and implements the MethodInterceptor interface. The spring class diagram is as follows: The interception function of the transaction interceptor depends on the implementation of the MethodInterceptor interface. Students familiar with spring must be familiar with the MethodInterceptor. This is the spring method interceptor, mainly based on the invoke method: 1 @Override 2 public Object invoke(final MethodInvocation invocation) throws Throwable { 3 // Work out the target class: may be {@code null}. 4 // The TransactionAttributeSource should be passed the target class 5 // as well as the method, which may be from an interface. 6 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); 7 8 // call TransactionAspectSupport of invokeWithinTransaction method 9 return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() { 10 @Override 11 public Object proceedWithInvocation() throws Throwable { 12 return invocation.proceed(); 13 } 14 }); 15 } As shown in figure TransactionInterceptor, the invoke method of the MethodInterceptor interface is duplicated and the invokeWithinTransaction() method of the parent class TransactionAspectSupport is invoked in the invoke method. The source code is as follows: 1 protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) 2 throws Throwable { 3 4 // If transaction attribute Empty,This method is a non transaction (non programmatic transaction) 5 final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); 6 final PlatformTransactionManager tm = determineTransactionManager(txAttr); 7 final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); 8 // Standard declarative transaction: if the transaction attribute is empty or non callback biasedTransaction manager 9 if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { 10 // Standard transaction demarcation with getTransaction and commit/rollback calls. 11 TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); 12 Object retVal = null; 13 try { 14 // Here is a surround enhancement, in this proceed You can define the enhanced implementation before and after 15 // Method execution 16 retVal = invocation.proceedWithInvocation(); 17 } 18 catch (Throwable ex) { 19 // According to the transaction definition, the exception needs to be rolled back, otherwise the transaction is committed 20 completeTransactionAfterThrowing(txInfo, ex); 21 throw ex; 22 } 23 finally {//Clear the current transaction information and reset it to the old one 24 cleanupTransactionInfo(txInfo); 25 }//Commit transactions before returning results 26 commitTransactionAfterReturning(txInfo); 27 return retVal; 28 } 29 // Programming transactions: (callback bias) 30 else { 31 final ThrowableHolder throwableHolder = new ThrowableHolder(); 32 33 // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. 34 try { 35 Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, 36 new TransactionCallback<Object>() { 37 @Override 38 public Object doInTransaction(TransactionStatus status) { 39 TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); 40 try { 41 return invocation.proceedWithInvocation(); 42 } 43 catch (Throwable ex) {// IfException needs to be rolled back 44 if (txAttr.rollbackOn(ex)) { 45 // If it is a runtime exception, return 46 if (ex instanceof RuntimeException) { 47 throw (RuntimeException) ex; 48 }// If it is any other exception, throw itThrowableHolderException 49 else { 50 throw new ThrowableHolderException(ex); 51 } 52 }// If rollback is not required 53 else { 54 // Define the exception and eventually commit the transaction directly 55 throwableHolder.throwable = ex; 56 return null; 57 } 58 } 59 finally {//Clear the current transaction information and reset it to the old one 60 cleanupTransactionInfo(txInfo); 61 } 62 } 63 }); 64 65 // Throw up anomaly 66 if (throwableHolder.throwable != null) { 67 throw throwableHolder.throwable; 68 } 69 return result; 70 } 71 catch (ThrowableHolderException ex) { 72 throw ex.getCause(); 73 } 74 catch (TransactionSystemException ex2) { 75 if (throwableHolder.throwable != null) { 76 logger.error("Application exception overridden by commit exception", throwableHolder.throwable); 77 ex2.initApplicationException(throwableHolder.throwable); 78 } 79 throw ex2; 80 } 81 catch (Throwable ex2) { 82 if (throwableHolder.throwable != null) { 83 logger.error("Application exception overridden by commit exception", throwableHolder.throwable); 84 } 85 throw ex2; 86 } 87 } 88 } As shown in the figure above, we mainly look at the first branch, declarative transaction. The core process is as follows: 1.createTransactionIfNecessary(): create a transaction if necessary 2.InvocationCallback's proceedWithInvocation(): InvocationCallback is the internal callback interface of the parent class, which is implemented in the subclass for the parent class to call, and invocationinterceptor in the subclass proceed(). Callback method execution 3. Abnormal rollback completeTransactionAfterThrowing() 1.createTransactionIfNecessary():1 protected TransactionInfo createTransactionIfNecessary( 2 PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { 3 4 // If you haven't defined a name, put the name of the connection point ID Defines the name of the transaction 5 if (txAttr != null && txAttr.getName() == null) { 6 txAttr = new DelegatingTransactionAttribute(txAttr) { 7 @Override 8 public String getName() { 9 return joinpointIdentification; 10 } 11 }; 12 } 13 14 TransactionStatus status = null; 15 if (txAttr != null) { 16 if (tm != null) { 17 status = tm.getTransaction(txAttr); 18 } 19 else { 20 if (logger.isDebugEnabled()) { 21 logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + 22 "] because no transaction manager has been configured"); 23 } 24 } 25 } 26 return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); 27 } The core is: 1) getTransaction(), get the TransactionStatus of the transaction according to the transaction attribute. All of them call platformtransactionmanager getTransaction(), see 3.3.1 for the source code. 2.invocation.proceed() callback business method:The final implementation class is ReflectiveMethodInvocation. The class diagram is as follows: As shown in the figure above, the reflective methodinvocation class implements the proxymethodinvocation interface, but proxymethodinvocation inherits the three-tier interface ProxyMethodInvocation->MethodInvocation->Invocation->Joinpoint Joinpoint: connection point interface, which defines the execution interface: object processed() throws throwable; Executes the current connection point and jumps to the next interceptor on the interceptor chain. Invocation: the calling interface, inherited from Joinpoint, defines the interface for obtaining parameters: Object[] getArguments(); It is a connection point with parameters that can be intercepted by interceptors. MethodInvocation: method calling interface, which inherits from Invocation and defines the interface of obtaining method: Method getMethod(); Is an interceptable connection point method with parameters. ProxyMethodInvocation: proxy method calling interface, which inherits from MethodInvocation and defines the interface for obtaining proxy object: Object getProxy(); Is a method call join point method executed by a proxy class. Reflective methodinvocation: implements the ProxyMethodInvocation interface, which naturally implements all interfaces of the parent class interface. Get the proxy class, get the method, get the parameters, use the proxy class to execute this method and automatically jump to the next connection point. Let's take a look at the source code of the proceed method: 1 @Override 2 public Object proceed() throws Throwable { 3 // Index at startup is-1,Wake up the connection point and increase it later 4 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { 5 return invokeJoinpoint(); 6 } 7 8 Object interceptorOrInterceptionAdvice = 9 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 10 if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 11 // Dynamic method matching verification is performed here. Static method matching has already been verified(MethodMatcherThere are two typical interfaces: dynamic/Static verification) 13 InterceptorAndDynamicMethodMatcher dm = 14 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; 15 if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { 16 return dm.interceptor.invoke(this); 17 } 18 else { 19 // Dynamic matching failed. Skip the current interception and enter the next one (interceptor chain) 21 return proceed(); 22 } 23 } 24 else { 25 // It is an interceptor, so we only call it:Before constructing this object, the pointcut will be statically evaluated. 27 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); 28 } 29 } What we finally call here is ((methodinterceptor) interceptorinterceptionadvice) invoke(this); The transaction interceptor is the transaction interceptor callback target business method (addUserBalanceAndUser). 3.completeTransactionAfterThrowing()Finally, call the rollback() of AbstractPlatformTransactionManager to commit the transaction commitTransactionAfterReturning() and finally call the commit() of AbstractPlatformTransactionManager. See 3.3.3 for the source codeSummary:It can be seen that whether it is a programmatic transaction or a declarative transaction, the final source code is to call the three methods of the PlatformTransactionManager interface of the transaction manager:
In the next section, let's take a look at how the transaction management implements these three methods.
3, Transaction core source codeLet's take a look at the core class diagram: As mentioned above, the PlatformTransactionManager top-level interface defines the most core transaction management methods. The following layer is the AbstractPlatformTransactionManager abstract class, which implements the methods of the PlatformTransactionManager interface and defines some abstract methods for subclass expansion. Finally, the following layer is two classic transaction managers: 1.DataSourceTransactionmanager, namely JDBC single database transaction manager, is implemented based on Connection, 2. JTA transaction manager, that is, multi database transaction manager (also known as distributed transaction manager), implements JTA specification and uses XA protocol for two-stage submission. We only look at the source code of DataSourceTransactionmanager based on JDBC connection. PlatformTransactionManager interface:
1. getTransaction get transactionAbstractPlatformTransactionManager implements getTransaction() method as follows:
As shown in the figure above, the source code is divided into two processing lines, 1. there are already transactions: isExistingTransaction() determines whether there is a transaction and there is transaction handleExistingTransaction (simultaneous interpreting) based on different propagation mechanisms. 2. no transactions exist at present: simultaneous interpreting of different communication mechanisms. The source code of handleExistingTransaction() is as follows: 1 private TransactionStatus handleExistingTransaction( 2 TransactionDefinition definition, Object transaction, boolean debugEnabled) 3 throws TransactionException { 4 // 1.NERVER(The current transaction is not supported;If the current transaction exists, throw an exception and report an error 5 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { 6 throw new IllegalTransactionStateException( 7 "Existing transaction found for transaction marked with propagation 'never'"); 8 } 9 // 2.NOT_SUPPORTED(Current transaction is not supported, existing synchronization will be suspended) 10 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { 11 if (debugEnabled) { 12 logger.debug("Suspending current transaction"); 13 } 14 Object suspendedResources = suspend(transaction); 15 boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); 16 return prepareTransactionStatus( 17 definition, null, false, newSynchronization, debugEnabled, suspendedResources); 18 } 19 // 3.REQUIRES_NEW Suspend the current transaction and create a new transaction 20 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { 21 if (debugEnabled) { 22 logger.debug("Suspending current transaction, creating new transaction with name [" + 23 definition.getName() + "]"); 24 }// Suspend current transaction 25 SuspendedResourcesHolder suspendedResources = suspend(transaction); 26 try {// Create a new transaction 27 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 28 DefaultTransactionStatus status = newTransactionStatus( 29 definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); 30 doBegin(transaction, definition); 31 prepareSynchronization(status, definition); 32 return status; 33 } 34 catch (RuntimeException beginEx) { 35 resumeAfterBeginException(transaction, suspendedResources, beginEx); 36 throw beginEx; 37 } 38 catch (Error beginErr) { 39 resumeAfterBeginException(transaction, suspendedResources, beginErr); 40 throw beginErr; 41 } 42 } 43 // 4.NESTED Nested transaction 44 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { 45 if (!isNestedTransactionAllowed()) { 46 throw new NestedTransactionNotSupportedException( 47 "Transaction manager does not allow nested transactions by default - " + 48 "specify 'nestedTransactionAllowed' property with value 'true'"); 49 } 50 if (debugEnabled) { 51 logger.debug("Creating nested transaction with name [" + definition.getName() + "]"); 52 }// Support savepoint:wrong JTA Transactions take this branch. AbstractPlatformTransactionManager Default is true,JtaTransactionManager The method is duplicated false,DataSourceTransactionmanager No copy, or true, 53 if (useSavepointForNestedTransaction()) { 54 // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. 55 DefaultTransactionStatus status = 56 prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); 57 status.createAndHoldSavepoint();// Create savepoint 58 return status; 59 } 60 else { 61 // JTA Transactions take this branch,Create a new transaction 62 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 63 DefaultTransactionStatus status = newTransactionStatus( 64 definition, transaction, true, newSynchronization, debugEnabled, null); 65 doBegin(transaction, definition); 66 prepareSynchronization(status, definition); 67 return status; 68 } 69 } 70 71 72 if (debugEnabled) { 73 logger.debug("Participating in existing transaction"); 74 } 75 if (isValidateExistingTransaction()) { 76 if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { 77 Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); 78 if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) { 79 Constants isoConstants = DefaultTransactionDefinition.constants; 80 throw new IllegalTransactionStateException("Participating transaction with definition [" + 81 definition + "] specifies isolation level which is incompatible with existing transaction: " + 82 (currentIsolationLevel != null ? 83 isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) : 84 "(unknown)")); 85 } 86 } 87 if (!definition.isReadOnly()) { 88 if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 89 throw new IllegalTransactionStateException("Participating transaction with definition [" + 90 definition + "] is not marked as read-only but existing transaction is"); 91 } 92 } 93 }// Come here PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED orPROPAGATION_MANDATORY,If a transaction exists, you can join the transaction, prepareTransactionStatus The third parameter is whether a new transaction is required. false Means you don't need anything new 94 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 95 return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); 96 } As shown in the figure above, when the current thread already has a transaction, the new processing of different isolation levels: 1.NERVER: current transaction is not supported; If the current transaction exists, an exception is thrown: "Existing transaction found for transaction marked with propagation 'never'" 1)suspend() 2)doBegin() 1) non JTA transaction: createAndHoldSavepoint() creates JDBC 3 0 savepoint, no synchronization required 2) JTA transaction: start a new transaction. Dobegin() + preparessynchronization() needs to be synchronized There are several core methods: suspend the current transaction () and start a new transaction doBegin(). The source code of suspend() is as follows: 1 protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException { 2 if (TransactionSynchronizationManager.isSynchronizationActive()) {// 1.Synchronization currently exists, 3 List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); 4 try { 5 Object suspendedResources = null; 6 if (transaction != null) {// Transaction is not empty, suspend transaction 7 suspendedResources = doSuspend(transaction); 8 }// Unbind various attributes of the current transaction: name, read-only, isolation level, and whether it is a real transaction. 9 String name = TransactionSynchronizationManager.getCurrentTransactionName(); 10 TransactionSynchronizationManager.setCurrentTransactionName(null); 11 boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); 12 TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); 13 Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); 14 TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); 15 boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); 16 TransactionSynchronizationManager.setActualTransactionActive(false); 17 return new SuspendedResourcesHolder( 18 suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); 19 } 20 catch (RuntimeException ex) { 21 // doSuspend failed - original transaction is still active... 22 doResumeSynchronization(suspendedSynchronizations); 23 throw ex; 24 } 25 catch (Error err) { 26 // doSuspend failed - original transaction is still active... 27 doResumeSynchronization(suspendedSynchronizations); 28 throw err; 29 } 30 }// 2.There is no synchronization, but the transaction is not empty. The transaction is suspended 31 else if (transaction != null) { 32 // Transaction active but no synchronization active. 33 Object suspendedResources = doSuspend(transaction); 34 return new SuspendedResourcesHolder(suspendedResources); 35 }// 2.There is no synchronization, but the transaction is empty and nothing needs to be done 36 else { 37 // Neither transaction nor synchronization active. 38 return null; 39 } 40 } doSuspend(), suspends the transaction. The AbstractPlatformTransactionManager abstract class doSuspend() will report an error: suspension is not supported. If the specific transaction executor supports it, copy doSuspend(), and the implementation of DataSourceTransactionManager is as follows: 1 @Override 2 protected Object doSuspend(Object transaction) { 3 DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; 4 txObject.setConnectionHolder(null); 5 return TransactionSynchronizationManager.unbindResource(this.dataSource); 6 } The core operations of suspending DataSourceTransactionManager transactions are: 1. Clear the connectionHolder database connection holder of the current transaction. 2. Unbind datasource from current thread In fact, ThreadLocal removes the corresponding variable (private static final ThreadLocal < map < object, Object > > resources = new namedthreadlocal < map < object, Object > > ("transactional resources") TransactionSynchronizationManager transaction synchronization manager. This class maintains multiple thread local variables ThreadLocal, as shown in the following figure:
The source code of doBegin() is as follows: 1 @Override 2 protected void doBegin(Object transaction, TransactionDefinition definition) { 3 DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; 4 Connection con = null; 5 6 try {// If the transaction has not been connection perhaps connection In the transaction synchronization state, reset the new connectionHolder 7 if (!txObject.hasConnectionHolder() || 8 txObject.getConnectionHolder().isSynchronizedWithTransaction()) { 9 Connection newCon = this.dataSource.getConnection(); 10 if (logger.isDebugEnabled()) { 11 logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); 12 }// Reset NEW connectionHolder 13 txObject.setConnectionHolder(new ConnectionHolder(newCon), true); 14 } 15 //Set the new connection as in transaction synchronization 16 txObject.getConnectionHolder().setSynchronizedWithTransaction(true); 17 con = txObject.getConnectionHolder().getConnection(); 18 //conn set transaction isolation level, read-only 19 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); 20 txObject.setPreviousIsolationLevel(previousIsolationLevel);//Set transaction isolation level for DataSourceTransactionObject 21 22 // If it is automatic submission, switch to manual submission 23 // so we don't want to do it unnecessarily (for example if we've explicitly 24 // configured the connection pool to set it already). 25 if (con.getAutoCommit()) { 26 txObject.setMustRestoreAutoCommit(true); 27 if (logger.isDebugEnabled()) { 28 logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); 29 } 30 con.setAutoCommit(false); 31 } 32 // If read-only, execute sql Set transaction read-only 33 prepareTransactionalConnection(con, definition); 34 txObject.getConnectionHolder().setTransactionActive(true);// set upconnection holderTransaction on state of 35 36 int timeout = determineTimeout(definition); 37 if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { 38 txObject.getConnectionHolder().setTimeoutInSeconds(timeout);// set upTimeout seconds 39 } 40 41 // binding connection To the current thread 42 if (txObject.isNewConnectionHolder()) { 43 TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); 44 } 45 } 46 47 catch (Throwable ex) { 48 if (txObject.isNewConnectionHolder()) { 49 DataSourceUtils.releaseConnection(con, this.dataSource); 50 txObject.setConnectionHolder(null, false); 51 } 52 throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); 53 } 54 } As shown in the figure above, the core operations of doBegin() are: 1.DataSourceTransactionObject "data source transaction object", set ConnectionHolder, and then set various properties for ConnectionHolder: automatic submission, timeout, transaction opening and isolation level. 2. Bind a thread local variable to the current thread, key=DataSource, data source = v=ConnectionHolder database connection.
2. commit transaction1, Before explaining the source code, take a look at the resource management class: SqlSessionSynchronization is an internal class of SqlSessionUtils, which inherits from the TransactionSynchronizationAdapter abstract class and implements the transaction synchronization interface TransactionSynchronization.The class diagram is as follows: The TransactionSynchronization interface defines the corresponding resource (JDBC transaction, then SqlSessionSynchronization) management method during transaction operation: 1 // Pending transactions Many of them use these interfaces to manage transactions. 2, commit transaction
The commit source code of AbstractPlatformTransactionManager is as follows: 1 @Override 2 public final void commit(TransactionStatus status) throws TransactionException { 3 if (status.isCompleted()) {// If the transaction is completed, an error is reported and cannot be committed again 4 throw new IllegalTransactionStateException( 5 "Transaction is already completed - do not call commit or rollback more than once per transaction"); 6 } 7 8 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; 9 if (defStatus.isLocalRollbackOnly()) {// If the transaction is explicitly marked for rollback, 10 if (defStatus.isDebug()) { 11 logger.debug("Transactional code has requested rollback"); 12 } 13 processRollback(defStatus);//Execute rollback 14 return; 15 }//If global rollback is not required, commit and global rollback 16 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { 17 if (defStatus.isDebug()) { 18 logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); 19 }//Execute rollback 20 processRollback(defStatus); 21 // Throw an "unexpected rollback exception" only at the outermost transaction boundary (new transaction) or when explicitly requested 23 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { 24 throw new UnexpectedRollbackException( 25 "Transaction rolled back because it has been marked as rollback-only"); 26 } 27 return; 28 } 29 // Execute commit transaction 30 processCommit(defStatus); 31 } As shown in the figure above, various judgments:
processCommit is as follows: 1 private void processCommit(DefaultTransactionStatus status) throws TransactionException { 2 try { 3 boolean beforeCompletionInvoked = false; 4 try {//3 Previous operations 5 prepareForCommit(status); 6 triggerBeforeCommit(status); 7 triggerBeforeCompletion(status); 8 beforeCompletionInvoked = true;//3Pre operation called 9 boolean globalRollbackOnly = false;//New transaction or global rollback failed 10 if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { 11 globalRollbackOnly = status.isGlobalRollbackOnly(); 12 }//1. There are savepoints, that is, nested transactions 13 if (status.hasSavepoint()) { 14 if (status.isDebug()) { 15 logger.debug("Releasing transaction savepoint"); 16 }//Release savepoint 17 status.releaseHeldSavepoint(); 18 }//2. New services 19 else if (status.isNewTransaction()) { 20 if (status.isDebug()) { 21 logger.debug("Initiating transaction commit"); 22 }//Call the transaction processor to commit the transaction 23 doCommit(status); 24 } 25 // 3.If the transaction is not a new transaction and the global rollback fails, but no exception is received when committing, an exception is thrown 27 if (globalRollbackOnly) { 28 throw new UnexpectedRollbackException( 29 "Transaction silently rolled back because it has been marked as rollback-only"); 30 } 31 } 32 catch (UnexpectedRollbackException ex) { 33 // After triggering, the transaction is synchronized and the status is rollback 34 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); 35 throw ex; 36 }// Transaction exception 37 catch (TransactionException ex) { 38 // Commit failed rollback 39 if (isRollbackOnCommitFailure()) { 40 doRollbackOnCommitException(status, ex); 41 }// After triggering the callback, the transaction synchronization status is unknown 42 else { 43 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); 44 } 45 throw ex; 46 }// Runtime exception 47 catch (RuntimeException ex) { As shown in the figure above, there are 6 core operations during commit transaction, including 3 pre operations and 3 post operations, as follows: 1.prepareForCommit(status); The source code is empty and has not been expanded at present. 2.triggerBeforeCommit(status); Trigger operation before submitting 1 protected final void triggerBeforeCommit(DefaultTransactionStatus status) { 2 if (status.isNewSynchronization()) { 3 if (status.isDebug()) { 4 logger.trace("Triggering beforeCommit synchronization"); 5 } 6 TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly()); 7 } 8 } The source code of triggerBeforeCommit is as follows: 1 public static void triggerBeforeCommit(boolean readOnly) { 2 for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) { 3 synchronization.beforeCommit(readOnly); 4 } 5 } As shown in the figure above, the TransactionSynchronizationManager class defines multiple ThreadLocal (thread local variables), one of which is used to save the transaction synchronization of the current thread: private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations"); Traverse the transaction synchronizer and execute the "before commit" operation for each transaction synchronizer. For example, the jdbc transaction we use will eventually be sqlsessionutils beforeCommit()->this. holder. getSqlSession(). commit(); Submit session. (because the source code is a spring management practice, transaction submission will not be executed in the end, for example, DefaultSqlSession: clear the cache and reset the state) 3.triggerBeforeCompletion(status); Trigger the operation before completion. If it is a jdbc transaction, it will eventually be, SqlSessionUtils.beforeCompletion-> TransactionSynchronizationManager.unbindResource(sessionFactory); Unbind the session factory of the current thread this.holder.getSqlSession().close(); Close the session. (because the source code is a spring management practice, the transaction close operation will not be performed in the end, such as DefaultSqlSession, and various clearing and closing operations will also be performed) 4.triggerAfterCommit(status); The action is triggered after the transaction is committed. TransactionSynchronizationUtils. triggerAfterCommit();-> TransactionSynchronizationUtils. Invokeaftercommit, as follows: 1 public static void invokeAfterCommit(List<TransactionSynchronization> synchronizations) { 2 if (synchronizations != null) { 3 for (TransactionSynchronization synchronization : synchronizations) { 4 synchronization.afterCommit(); 5 } 6 } 7 } Well, after a meal, it was copied in the transaction synchronization adapter and empty SqlSessionSynchronization inherits the TransactionSynchronizationAdapter but does not replicate this method. 5. triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); TransactionSynchronizationUtils.TransactionSynchronizationUtils.invokeAfterCompletion, as follows: 1 public static void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) { 2 if (synchronizations != null) { 3 for (TransactionSynchronization synchronization : synchronizations) { 4 try { 5 synchronization.afterCompletion(completionStatus); 6 } 7 catch (Throwable tsex) { 8 logger.error("TransactionSynchronization.afterCompletion threw exception", tsex); 9 } 10 } 11 } 12 } After completion: for JDBC transactions, the final: 1) If the session is still alive, close the session, 2) Reset various attributes: referenceCount reference count of SQL session holder of SQL session synchronizer, synchronizedWithTransaction synchronization transaction, rollback only, deadline timeout point. 6.cleanupAfterCompletion(status); 1) Set the transaction status to completed. 2) If it is a new transaction synchronization, unbind the database resources bound by the current thread and reset the database connection 3) If there is a pending transaction (nested transaction), wake up various resources of the pending old transaction: database resources and synchronizer. 1 private void cleanupAfterCompletion(DefaultTransactionStatus status) { 2 status.setCompleted();//Set transaction status complete For DataSourceTransactionManager, the source code of doCleanupAfterCompletion is as follows: 1 protected void doCleanupAfterCompletion(Object transaction) { 2 DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; 3 4 // If it is the latest connection holder, unbind the current thread<Database resources,ConnectionHolder> 5 if (txObject.isNewConnectionHolder()) { 6 TransactionSynchronizationManager.unbindResource(this.dataSource); 7 } 8 9 // Reset database connection (isolation level, read-only) 10 Connection con = txObject.getConnectionHolder().getConnection(); 11 try { 12 if (txObject.isMustRestoreAutoCommit()) { 13 con.setAutoCommit(true); 14 } 15 DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); 16 } 17 catch (Throwable ex) { 18 logger.debug("Could not reset JDBC Connection after transaction", ex); 19 } 20 21 if (txObject.isNewConnectionHolder()) { 22 if (logger.isDebugEnabled()) { 23 logger.debug("Releasing JDBC Connection [" + con + "] after transaction"); 24 }// Resource reference count-1,Close database connection 25 DataSourceUtils.releaseConnection(con, this.dataSource); 26 } 27 // Reset all properties of the connection holder 28 txObject.getConnectionHolder().clear(); 29 }
3. rollback rollback transactionThe source code of rollback in AbstractPlatformTransactionManager is as follows: 1 public final void rollback(TransactionStatus status) throws TransactionException { 2 if (status.isCompleted()) { 3 throw new IllegalTransactionStateException( 4 "Transaction is already completed - do not call commit or rollback more than once per transaction"); 5 } 6 7 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; 8 processRollback(defStatus); 9 } The source code of processRollback is as follows: 1 private void processRollback(DefaultTransactionStatus status) { 2 try { 3 try {// Unbind the session factory bound by the current thread and close the session 4 triggerBeforeCompletion(status); 5 if (status.hasSavepoint()) {// 1.If there is a savepoint, it is a nested transaction 6 if (status.isDebug()) { 7 logger.debug("Rolling back transaction to savepoint"); 8 }//Rollback to savepoint 9 status.rollbackToHeldSavepoint(); 10 }//2. If it is a simple transaction 11 else if (status.isNewTransaction()) { 12 if (status.isDebug()) { 13 logger.debug("Initiating transaction rollback"); 14 }//Rollback core method 15 doRollback(status); 16 }//3. There is a current transaction and there is no savepoint, that is, to join the current transaction 17 else if (status.hasTransaction()) {//If it has been marked for rollback or when the join transaction failsGlobal rollback (default) true) 18 if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { 19 if (status.isDebug()) {//debug Print: failed to join the transaction-Mark existing transaction as rollback 20 logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); 21 }//Set the current connectionHolder: roll back when joining an existing transaction 22 doSetRollbackOnly(status); 23 } 24 else { 25 if (status.isDebug()) { 26 logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); 27 } 28 } 29 } 30 else { 31 logger.debug("Should roll back transaction but cannot - no transaction available"); 32 } 33 } 34 catch (RuntimeException ex) {//Close the session and reset the SqlSessionHolder property 35 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); 36 throw ex; 37 } 38 catch (Error err) { 39 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); 40 throw err; 41 } 42 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); 43 } 44 finally {,,Unbind the current thread 45 cleanupAfterCompletion(status); 46 } 47 } As shown in the figure above, there are several common methods consistent with the transaction submission, so they will not be repeated. Here we mainly look at doRollback. The source code of doRollback() of DataSourceTransactionManager is as follows: 1 protected void doRollback(DefaultTransactionStatus status) { 2 DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); 3 Connection con = txObject.getConnectionHolder().getConnection(); 4 if (status.isDebug()) { 5 logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); 6 } 7 try { 8 con.rollback(); 9 } 10 catch (SQLException ex) { 11 throw new TransactionSystemException("Could not roll back JDBC transaction", ex); 12 } 13 } Well, it's not complicated at all. It's the rollback of Connection IV. sequence diagramSpecially sorted out the sequence diagram (simple new transactions, no savepoints, etc.) as follows:
===========Reference======== Chapter 4 of Spring Combat 4: aspect oriented Spring |
|
|