Spring @Transactional Deep Analysis of Self-Calling Problems
The nature of the problem: Self-call transaction fails
When method A inside class calls another class with@Transactional
When the method B of the annotation is explained, the transaction annotation will not take effect. This is because Spring's transaction management is implemented based on AOP proxy, and self-calling will bypass the proxy mechanism.
Principle analysis
1. Spring transaction implementation mechanism
Spring transactions are implemented through dynamic proxying, and there are two ways:
- JDK dynamic proxy: Based on interface
- CGLIB Agent: Based on class inheritance
// Original call process (expected transaction process)caller → Proxy object → Target object.methodB() // The actual process during self-callcaller → Target object.methodA() → Target object.methodB() [Bypass the proxy]
2. Self-call problem example
@Service public class OrderService { public void placeOrder(Order order) { // Self-call causes transaction failure validateOrder(order); // Other business logic... } @Transactional public void validateOrder(Order order) { // Database verification operation... } }
Solution
Solution 1: Inject your own agent (recommended)
@Service public class OrderService { @Autowired private OrderService selfProxy; // Inject proxy object public void placeOrder(Order order) { (order); // Call via proxy } @Transactional public void validateOrder(Order order) { // The transaction takes effect } }
Solution 2: Refactoring the code structure
@Service @RequiredArgsConstructor public class OrderService { private final OrderValidator orderValidator; public void placeOrder(Order order) { (order); } } @Service class OrderValidator { @Transactional public void validate(Order order) { // Transaction operation } }
Solution 3: Use AspectJ mode (weaving at compile time)
# -target-class=true =false
Technical depth: Spring transaction proxy mechanism
Agent creation process
- Create original beans when container starts
- pass
AbstractAutoProxyCreator
Create a proxy - right
@Transactional
Methods to add interceptors
Transaction interceptor call stack
() → () → () → Final call to the target method
Production environment best practices
Unify transaction boundaries:
@Service @Transactional // Class level annotationpublic class OrderService { public void placeOrder() { // All public methods have transactions by default } }
Transaction monitoring:
@Aspect @Component public class TransactionMonitor { @Around("@annotation(transactional)") public Object monitor(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable { // Record transaction start/end } }
Exception handling:
@Transactional(rollbackFor = {, }) public void process() { // Identify the rollback exception type}
Common Mistakes
Private method annotation:
@Transactional // Invalid!private void internalMethod() {}
Final method annotation:
@Transactional // Invalid under CGLIB proxy!public final void finalMethod() {}
Similar non-transactional methods call transaction methods:
public void methodA() { methodB(); // Transaction invalidation} @Transactional public void methodB() {}
Performance considerations
- Agent creation increases startup time
- Each transaction method call has an interception overhead
- Long transactions will occupy database connections
This is the end of this article about the in-depth analysis of Spring @Transactional self-calling problem. For more related Spring @Transactional self-calling content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!