Spring transaction is a technology that we often use in our daily work. Spring provides three ways for us to use Spring transaction, including programming, annotation and AOP aspect. Programmatic transaction is not recommended because of its large invasion of code. We use annotations as an example to analyze the principle and source code implementation of Spring transactions.

First let’s take a quick look at how Spring transactions are used.

<tx:annotation-driven transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/>  </bean>

Put the @Transactional annotation on any method that needs to open a transaction. Note that when the < TX :annotation-driven/> tag does not specify the transaction-Manager attribute, By default, a bean with a fixed id transactionManager is looked for as the transactionManager. If there is no bean with a transactionManager id and no value (transactionManager) is specified when the @transactional annotation is used, the program will report an error. When we configure more than two < TX :annotation-driven/> tags, it looks like this:

<tx:annotation-driven transaction-manager="transactionManager1"/>
<bean id="transactionManager1" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource1"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager2"/>
<bean id="transactionManager2" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource2"/>
</bean>

This is when the first <tx:annotation-driven/> takes effect, which means that when we use the @Transactional annotation we do not specify a transaction manager. The default transaction manager is transactionManager1.

Next, we will start to analyze the relevant source code of Spring. First, we will take a look at the parsing of < TX :annotation-driven/> tags. Here we need readers to have a certain understanding of the process of parsing Spring custom tags. Locking TxNamespaceHandler:

(Right-click to view larger image)

This method is long, the key part is marked, and the outermost if judgment limits the < TX :annotation-driven/> tag to being parsed only once, so only the first parsed tag will take effect. Three BeanDefinitions are registered in the blue box, AnnotationTransactionAttributeSource, TransactionInterceptor and BeanFactoryTransactionAttributeSourceAdvisor respectively, Add the first two BeanDefinitions to the properties of the third BeanDefinition, and these three beans support the entire transaction functionality, as described below. Let’s look at the first method in the red box:

Remember that when the < TX: Annotation-driven /> tag does not specify the transaction-Manager attribute, it defaults to looking for a bean with a fixed ID named transactionManager as the transaction manager, which is implemented here. Let’s look at the second method of the red box:

The main purpose of these two methods is registered InfrastructureAdvisorAutoProxyCreator, registered what is the purpose of this class? Let’s look at the hierarchy of this class:

We found this kind of indirect implements the BeanPostProcessor interface, as we know, Spring will ensure that all the bean will be called when instantiating the postProcessAfterInitialization method, we can use this method of packing and change the bean, This method is implemented in its AbstractAutoProxyCreator parent: AbstractAutoProxyCreator class AbstractAutoProxyCreator

The beanFactory.getBean method retrieves the beannames of all Advisor classes and returns them. Don’t know whether you still remember at the beginning of the article mentioned three classes, including BeanFactoryTransactionAttributeSourceAdvisor Advisor interface is achieved, so the bean will be extracted here, And the other two bean is woven into the BeanFactoryTransactionAttributeSourceAdvisor, so also can be extracted together, BeanFactoryTransactionAttributeSourceAdvisor class hierarchy is illustrated below:

Let’s see how Spring gets the matching enhancer from all the candidate enhancers:

The above method mentioned the concept of an introduction enhancement, which is briefly explained here, is a special type of enhancement. Instead of weaving an enhancement around a target method, it creates new methods and properties for the target class, so the join point for an introduction enhancement is class-level, not method-level. Through introduction enhancement, we can add an implementation of an interface to the target class, that is, the target class does not implement an interface, through introduction enhancement, we can create a proxy for the target class to implement the interface, see the reference link at the end of the article. The other method uses two overloaded canApply methods to find a matching enhancer for the target class, where the first canApply method calls the second canApply method and passes the third argument false:

In BeanFactoryTransactionAttributeSourceAdvisor class hierarchy above we see it implements PointcutAdvisor interface, so they will call in the red box canApply method of judgement, The first parameter pca. GetPointcut () is called BeanFactoryTransactionAttributeSourceAdvisor getPointcut method:

TransactionAttributeSource here is what we see at the beginning of the article for BeanFactoryTransactionAttributeSourceAdvisor weave AnnotationTransactionA of two beans TtributeSource, we canApply to TransactionAttributeSourcePointcut as the first parameter to continue tracking method:

We track PC. GetMethodMatcher () method is TransactionAttributeSourcePointcut getMethodMatcher method is implemented in its parent class:

Discovery method directly back to this, that is, the following methodMatcher. Matches method is invoked TransactionAttributeSourcePointcut matches:

We see on the tas is AnnotationTransactionAttributeSource actually, actually is also the purpose of judging our business method or is there a @ Transactional annotation on the class, Tracking AnnotationTransactionAttributeSource getTransactionAttribute method:

The transaction declaration in the method has the highest priority. If there is no declaration on the method, look for it on the class:

This annotationParsers is initialized when AnnotationTransactionAttributeSource class initialization:

So annotationParser. ParseTransactionAnnotation is called SpringTransactionAnnotationParser parseTransactionAnnotation method:

Transactional annotation: Transactional annotation Transactional annotation Transactional annotation

In this method we see the resolution of various common and unusual attributes declared in the Transactional annotation. Now that the Transactional initialization is complete, it’s time to actually execute.

The wrapIfNecessary method, AbstractAutoProxyCreator, is an AbstractAutoProxyCreator class that creates a proxy for the bean after obtaining the object bean’s enhancer. When the target method of the proxy class is executed, the Advisor’s getAdvice is called to get the MethodInterceptor and its invoke method is executed, And we are the leading role of this article BeanFactoryTransactionAttributeSourceAdvisor getAdvice method returns we begin to see the article for the woven into another bean, namely TransactionInterceptor, It implements MethodInterceptor, so we examine its invoke method:

This method is long, but the overall logic is very clear. The preference is to get transaction properties, GetTransactionAttrubuteSource here () method of the return value is also at the beginning of the article we see is woven into the AnnotationTransactionAttributeSource TransactionInterceptor, In preparation for the transaction has been parsed the transaction attribute and save in the cache, so there will be directly from the cache, then obtain configuration TransactionManager, namely determineTransactionManager method, If transaction-Manager is not specified and there is no bean with the default ID transactionManager, an error is reported, and the transaction information is created and the target method is executed for different processing of declarative and programmatic transactions. Finally, the rollback or commit operation is performed based on the execution result. We first analyze the process of creating a transaction. Before the analysis, I hope you can first understand the Spring transaction propagation behavior, which is helpful to understand the source code below. Here is a brief introduction. For more detailed information, please refer to the Spring official documentation, which has updated the detailed introduction.

Spring’s transaction Propagation behavior is defined in the enumeration class “Propagation”.

REQUIRED: Business methods need to run in a container. If the method is already in a transaction when it runs, join the transaction, or create a new transaction yourself, which is the default transaction propagation behavior.

NOT_SUPPORTED: The declared method does not require a transaction. If the method is not associated with a transaction, the container will not start the transaction for it, and if the method is called within a transaction, the transaction will be suspended, and the original transaction will resume execution after the call.

REQUIRESNEW: The method assembly initiates a new transaction for itself, regardless of whether a transaction exists. If the method is already running in a transaction, the original transaction suspends and a new transaction is created.

MANDATORY: This method can only be executed in an existing transaction. Business methods cannot initiate their own transactions. The container throws an exception if it is called without a transaction.

SUPPORTS: When this method is called within the scope of a transaction, the method becomes part of that transaction. If a method is called outside the scope of the transaction, the method is executed without a transaction.

NEVER: This method must NEVER be executed within the scope of a transaction. If yes, throw the exception. The method is executed only if it is not associated with any transactions.

NESTED: Runs in a NESTED transaction if an active transaction exists. If there are no active transactions, the REQUIRED attribute is executed. It uses a single transaction that has multiple savepoints that can be rolled back. A rollback of an internal transaction has no effect on an external transaction. It is only right DataSourceTransactionManager transaction manager work.

To determine whether the current thread has a transaction is to determine whether the recorded database connection is empty and the transactionActive status is true.

REQUIRESNEW starts a new transaction and suspends the original one, of course starting a new transaction requires a new database connection:

The suspend operation sets the current connectionHolder to NULL, saves the original transaction information, and resets the current transaction information. Let’s see how Spring starts a new transaction:

If it is a new transaction, it needs to acquire a new database connection and set its isolation level, whether it is read-only, etc. Here is how to log transaction information to the current thread:

The next step is to log the transaction state and return transaction information:

Then we will execute the target business method, commit or rollback according to the execution result, let’s first look at the rollback operation:

The default rollback condition is RuntimeException or Error, which can be configured by ourselves.

Savepoints are generally used for embedded transactions, and rollback of embedded transactions does not cause rollback of external transactions. Let’s look at the rollback of the new transaction:

It is simple to take the current thread’s database connection and call its rollback method to rollback, using the API provided by the underlying database connection. Finally, there is another operation to clean up and resume pending transactions:

If a transaction is suspended before the execution of a transaction, the suspended transaction needs to be recovered after the execution of the current transaction. The original transaction information is saved and reset during the suspension of the transaction. Therefore, the recovery operation is to set the current transaction information to the saved original transaction information. At this point, the rollback of the transaction is complete, so let’s look at the commit of the transaction:

In the analysis of the rollback process above, we mentioned that if the current transaction is not an independent transaction and there is no savepoint, only a rollback mark is set during the rollback, and the whole transaction will be rolled back when the external transaction commits.

The commit operation is also as simple as calling the commit method of the database connection underlying API.


Reference links:

http://blog.163.com/asd_wll/blog/static/2103104020124801348674/

https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#spring-data-tier


This article is protected by the original, without authorisation, prohibit reprinting. Linkedkeeper.com (By Zhang Qiang)