Spring transactions caused by JDK dynamic agent do not work

Source: Internet
Author: User
Tags commit rollback
Original address: Https://mp.weixin.qq.com/s/vCZP8sPrtnXWvg6IlcHQOg
first, scene analysis

Recent projects have encountered a very strange problem, the general business scenario is this: we first set two transactions, the transaction parent and transaction child, in the controller inside the same time call both methods, the sample code is as follows:

1. Scenario A:

There are actually two things executed, the result of which is that two methods can be inserted into the data. As follows:

2. Scene B:

Modify the above code as follows:

The meaning of propagation.requires_new is that if a transaction is currently present, the current transaction is suspended and a new thing is opened to continue execution, after the new thing has been executed, and then the transaction suspended before the probation, if there is no current transaction, then a new thing is opened.

The result of execution is that all two methods can insert data. The results of the implementation are as follows:

Scenario A and scene B are performed normally, and no rollback occurs during the period, if an exception occurs in the child () method.

3. Scene C

The code to modify Child () is as follows, and the other code is the same as scenario B:

The execution results are as follows, an exception occurs, and the data is not inserted in:

Question 1: the child () in scene C throws an exception, but the parent () does not throw an exception, as if the parent () commit succeeds and the child () is rolled back.

There may be small partners to say, child () throws an exception in the parent () is not caught, causing the parent () also throws an exception. So both of them will roll back.

4. Scene d

In accordance with the above-mentioned small partner's question this time, if the parent () method is modified to capture the exception thrown in child (), the other code is the same as scenario C:

And then execute it again, and the result is that two of them are inserted into the database:

Seeing a lot of small partners here might ask, in our logic, that the exception is thrown in child (), and the parent () does not throw and catches the child () throws an exception. The result of the execution should be the child () rollback, and the parent () commit succeeds.

Question 2: Why is scene D not child () rollback and parent () submission successful?

The scenario C and scene D above seem to blend in with either success or failure. It's not the same as the effect we expected. See here This is the topic that we are going to discuss today, "the JDK dynamic agent buries a hole in the spring transaction." Next we analyze the deep-seated reasons that spring things cannot be rolled back in that particular scenario.

Second, the nature of the problem lies

We know that spring transaction management is implemented through the JDK dynamic proxy (the other is implemented using the Cglib dynamic proxy). It is also because of the nature of the dynamic proxy that the parent () method called the Child () method causes the transaction in the child () method to fail. In a nutshell, when the parent () method calls the child () method in Scene D, the transaction of the child () method does not work, and the child () method is like a normal method without a transaction, essentially equivalent to the following code:

Scene C Nature:

Scene D Essence:

As the above code, we can easily explain questions 1 and 2, because the nature of the dynamic proxy causes scenes C and scenes D to be the essence of the above code. In Scene C, Child () throws an exception without catching, equivalent to throwing an exception in the parent transaction, causing the parent () to roll back together, because they are essentially the same method; in Scene D, Child () throws an exception and catches it, and no exception is thrown in the parent transaction. The parent () and child () are in a transaction, so they all succeed;

See here, what exactly is this feature of the dynamic proxy that will cause the spring transaction to fail that. Third, the dynamic agent of this feature is exactly what.

First, let's look at a simple dynamic proxy implementation:

At this point we execute the following test methods, noting that Test1 () and Test2 () are called at the same time, and the execution results are as follows:

As you can see, in the Orderserviceimpl class, because Test1 () does not call Test2 (), their methods are executed using proxies, which means that both test1 and Test2 are Invoke () methods invoked through proxy objects, which are similar to our scenes A and B.

Join us to simulate the scene C and Scene D in Test1 () call Test2 (), then the code is modified as follows:

The results of the implementation are as follows:

Here can be very clear to see test1 () Go is the agent, and Test2 () go is the ordinary method, not through the agent. See here whether you have come to understand the big.

This should be a good way to understand why this is so. This is because the method in Test1 () called Test2 () in Java is essentially equivalent to putting the method body of the test2 () into test1 (), which is the internal method, the same no matter how many layers you nest, only the proxy object proxy The direct invocation of the method is the real walk proxy, as follows:

The test method is the same as the test method above, and the result is as follows:

Remember: Only proxy object proxies directly call that method is the real go proxy. Iv. How to solve the pit.

In the above analysis, we have already learned the question of why transactions cannot be rolled back when using spring transactions in this particular scenario, let's talk about several workarounds:

1, we can choose to evade this problem. The simplest way to solve a problem without using this kind of transaction nesting is to solve the problem by referring to the service or to the more forward logic, which is not the case with service.xxxtransaction.

2. Get the proxy object through the Aopproxy context:

(1) springboot configuration mode: annotation on exposeproxy = True, exposing the proxy object (otherwise Aopcontext.currentproxy ()) throws an exception.

Add Dependencies:

Add Annotations:

Modifying the original code is performed as follows:

The result of this is:

It can be seen that the child method has been rolled back because of an exception, and the parent is able to commit correctly, which is the result we want. Note that when the parent calls child, the exception is caught by Try/catch.

(2) The traditional spring XML configuration file only needs to add the configuration of the dependent settings, as follows:

<aop:aspectj-autoproxy expose-proxy= "true"/>

3, through the ApplicationContext context to solve:


The implementation results are in line with our expectations:


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.