Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the execution process of Spring transaction

2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

In view of what the Spring transaction execution process is, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and feasible method.

Let's start with a few points in the startup process:

Load the configuration file:

AbstractAutowireCapableBeanFactory.doCreateBean-- > initializeBean-- > applyBeanPostProcessorsAfterInitialization-- > beanProcessor.postProcessAfterInitialization-- > AbstractAutoProxyCreator.postProcessAfterInitialization-- > wrapIfNecessary (bean, beanName, cacheKey)-- > getAdvicesAndAdvisorsForBean (bean.getClass (), beanName, null) Advisor is the configuration of Pointcut and Advice, which includes Pointcut and Advice, and is the code that injects Advice into the Pointcut location in the program. AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary: call the transaction notification in the figure above txAdvice to set the data source DataSourceTransactionManager,ChooseDataSource is the default data source configured in the project and inherited to AbstractRoutingDataSource, named or something:

After startup, initiate a transaction call. First, intercept the method from CglibAopProxy.intercept-> ReflectiveMethodInvocation.proceed-- > ExposeInvocationInterceptor.invoke-- > TransactionInterceptor.invoke:

Public Object invoke (final MethodInvocation invocation) throws Throwable {/ / Work out the target class: may be {@ code null}. / / The TransactionAttributeSource should be passed the target class / / as well as the method, which may be from an interface. Class targetClass = (invocation.getThis ()! = null? AopUtils.getTargetClass (invocation.getThis ()): null); / / Adapt to TransactionAspectSupport's invokeWithinTransaction... Return invokeWithinTransaction (invocation.getMethod (), targetClass, new InvocationCallback () {@ Override public Object proceedWithInvocation () throws Throwable {return invocation.proceed ();}});}

TransactionAspectSupport.invokeWithinTransaction:

The determineTransactionManager method first determines whether the current transaction has a specific transaction manager configured, and if no default transaction manager has been set, there is no such case:

Public PlatformTransactionManager getTransactionManager () {return this.transactionManagerCache.get (DEFAULT_TRANSACTION_MANAGER_KEY);}

Next, determine whether the loaded transaction attribute exists or whether the current transaction manager is not CallbackPreferringPlatformTransactionManager. If the condition is met, it will be executed to createTransactionIfNecessary. First:

If (txAttr! = null & & txAttr.getName () = null) {txAttr = new DelegatingTransactionAttribute (txAttr) {@ Override public String getName () {return joinpointIdentification;}};}

DelegatingTransactionAttribute itself has no other purpose than to be used after implementation. Then the transaction is fetched from the transaction manager, and doGetTransaction fetches the connection set from the previous set data source to DataSourceTransactionObject:

Protected Object doGetTransaction () {DataSourceTransactionObject txObject = new DataSourceTransactionObject (); txObject.setSavepointAllowed (isNestedTransactionAllowed ()); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource (this.dataSource); txObject.setConnectionHolder (conHolder, false); return txObject;}

Then determine whether the transaction already exists, and if it exists, such as a nested transaction, it will be processed according to the defined propagation mode, which will be said later, which does not exist yet. Then verify that the transaction timed out. Because of the transaction propagation method taken from the transaction definition (objects whose TransactionDefinition holds transaction attributes such as isolation level), I have the default PROPAGATION_REQUIRED here. The first sentence used to suspend the transaction does nothing, then maybe transaction synchronization is activated, and then it's time to start the transaction DataSourceTransactionManager doBegin:

If (txObject.getConnectionHolder ()) = = null | | txObject.getConnectionHolder () .isSynchronizedWithTransaction () {Connection newCon = this.dataSource.getConnection (); if (logger.isDebugEnabled ()) {logger.debug ("Acquired Connection [" + newCon + "] for JDBC transaction");} txObject.setConnectionHolder (new ConnectionHolder (newCon), true) }

First getConnection,set to DataSourceTransactionObject from the data source and set it to synchronous transaction. PrepareConnectionForTransaction determines whether the transaction is read-only according to the configuration, and the isolation level txObject.setPreviousIsolationLevel (previousIsolationLevel) of the previous transaction in the nested transaction:

It is a bit of a mouthful to determine whether the transaction object has been set to auto commit, if so, which probably means that after the first transaction commits when the transaction reuses the database connection, the next transaction of the same connection is still set to auto commit, otherwise, if the current transaction is set to hand pain commit, because the connection in the connection pool will be reused. Subsequent transactions on the same connection may need to manually call conn.commit to commit the next transaction, setting that the connected transaction represented by connection holder is active Check the timeout; determine whether the connection of the current transaction is newly created, register with TransactionSynchronizationManager and bind the thread to the transaction through ThreadLocal; prepareSynchronization sets whether the transaction synchronization manager contains the actual executed transaction, the current thread transaction isolation level, whether read-only, and the transaction definition name, initialize the synchronous transaction (private static final ThreadLocal synchronizations = new NamedThreadLocal ("transaction"):

Public static void initSynchronization () throws IllegalStateException {if (isSynchronizationActive ()) {throw new IllegalStateException ("Cannot activate transaction synchronization-already active");} logger.trace ("Initializing transaction synchronization"); synchronizations.set (new LinkedHashSet ());}

Go back to the TransactionAspectSupport,prepareTransactionInfo method to create the transaction information and bind it to the current thread through ThreadLocal:

TransactionInfo txInfo = new TransactionInfo (tm, txAttr, joinpointIdentification); private void bindToThread () {/ / Expose current TransactionStatus, preserving any existing TransactionStatus / / for restoration after this transaction is complete. This.oldTransactionInfo = transactionInfoHolder.get (); transactionInfoHolder.set (this);}

The current transaction state is retained in the binding and should be used to restore the site of the outer transaction after the inner transaction is completed in the nested transaction.

When the createTransactionIfNecessary ends, I go back to the invokeWithinTransaction method of TransactionAspectSupport, and then there is invocation.proceedWithInvocation, which is posted on it, and this method is implemented when new InvocationCallback. There is only one sentence in the code that calls the proceed of MethodInvocation, that is, the method that calls the cut method around the notification (it is also possible to call the cut method of other Interceptor). I call the cut method directly, but I don't go directly to the completeTransactionAfterThrowing or commitTransactionAfterReturning behind it, nor do I have the method to clean up the transaction information. Because there are nested transactions, so it is nested cut, do not say the same with the above process, talk about the different.

This time, because getTransaction already has a transaction and uses the same connection, the JdbcTransactionObjectSupport instance saves the connectionHolder, so this time it enters this branch of judgment:

If (isExistingTransaction (transaction)) {/ / Existing transaction found-> check propagation behavior to find out how to behave. Return handleExistingTransaction (definition, transaction, debugEnabled);}

The handleExistingTransaction method takes different branches of code according to different transaction propagation, which means that REQUIRED uses existing transactions by default, so this method is basically the same as not leaving. When the nesting is done, you go to the cleanupTransactionInfo method, which actually calls TransactionInfo.restoreThreadLocalStatus and actually restores the previous transaction information:

Private void restoreThreadLocalStatus () {/ / Use stack to restore old transaction TransactionInfo. / / Will be null if none was set. TransactionInfoHolder.set (this.oldTransactionInfo);}

Then there is commitTransactionAfterReturning:

/ * Execute after successful completion of call, but not after an exception was handled. * Do nothing if we didn't create a transaction. * @ param txInfo information about the current transaction * / protected void commitTransactionAfterReturning (TransactionInfo txInfo) {if (txInfo! = null & & txInfo.hasTransaction ()) {if (logger.isTraceEnabled ()) {logger.trace ("Completing transaction for [" + txInfo.getJoinpointIdentification () + "]");} txInfo.getTransactionManager () .commit (txInfo.getTransactionStatus ());}}

Commit method in AbstractPlatformTransactionManager, the inner transaction is committed first. It should be mentioned here that the subtransaction of the nested transaction reports an error but does not throw it to the outer transaction, which may lead to the problem of rollback-only. DefStatus.isLocalRollbackOnly () is to determine whether an inner transaction has an error setting rollbackOnly to true. In addition, with regard to global transactions, it seems to be using a two-segment XA? However, it is not available at the moment, it is just a connection to the database, which can be considered here. It's time to specifically deal with the submission method processCommit. Also in AbstractPlatformTransactionManager. The prepareForCommit method is empty, protected should be ready to be overridden for subclasses, or that's what I'm looking for. Savepoint does not. Because it is an inner transaction, the triggerBeforeCommit, triggerBeforeCompletion, triggerAfterCommit, and triggerAfterCommit methods are not executed. Once the transaction state is set, the inner transaction is committed.

ExposeInvocationInterceptor (which exposes the interceptor chain, which is generally not needed and should be at the head of the chain when used) restores the execution of the intercepted method on the outer layer:

@ Override public Object invoke (MethodInvocation mi) throws Throwable {MethodInvocation oldInvocation = invocation.get (); invocation.set (mi); try {return mi.proceed ();} finally {invocation.set (oldInvocation);}}

This outer layer is the method that is actually intercepted and will be executed through CglibAopProxy.

Next, the outer transaction is committed. The old of cleanupTransactionInfo is null this time, not to mention the same process. Because the outer transaction creates a synchronization object, triggerBeforeCommit executes:

TransactionSynchronizationUtils.triggerBeforeCommit (status.isReadOnly ()); public static void triggerBeforeCommit (boolean readOnly) {for (TransactionSynchronization synchronization: TransactionSynchronizationManager.getSynchronizations ()) {synchronization.beforeCommit (readOnly);}}

The beforeCommit here is because I use mybatis, so if it is executed, it will be executed to SqlSessionUtils.beforeCommit. However, because SqlSessionUtils cannot be inherited, it is not easy to tamper here, so we can only find a way outside this class. TriggerBeforeCompletion is similar. The outer transaction will have to get status.isGlobalRollbackOnly () for doCommit (status) after the error is reported, note that it does not actually interrupt the execution of the commit. DoCommit (status):

@ Override protected void doCommit (DefaultTransactionStatus status) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction (); Connection con = txObject.getConnectionHolder () .getConnection (); if (status.isDebug ()) {logger.debug ("Committing JDBC transaction on Connection [" + con + "]");} try {con.commit () } catch (SQLException ex) {throw new TransactionSystemException ("Could not commit JDBC transaction", ex);}}

Yes, we use druid, so there is no need to explain here. After that, some callbacks are completed, such as unbinding, clear and reset. The previous settings must be restored to auto-commit and will be restored in doCleanupAfterCompletion, and finally the connection will be closed.

The answer to the question about the execution process of Spring transaction is shared here. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel to learn more about it.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report