@Transactional annotation internal call invalid

Scenes

Although insertOrder has the @Transactional annotation, it is called by the internal method insert, the transaction is ignored, and the abnormal transaction will not be rolled back.
The above two problems @Transactional annotations only apply to public methods and self-invocation problems, which are caused by using Spring AOP proxy.

@Controller
class XService {
    
    
    @Autowired
    private YService yService;
    public void doOutside(){
    
    
        this.doInside(); //或者直接doInside();效果是一样的
    }
    @Transactional
    public  void doInside(){
    
    
        //do sql statement
    }
}
@Controller
class Test {
    
    
    @Autowired
    private XService xService;
    public void test(){
    
    
        xService.doOutside();
    }
}

Recalling the Java foundation, this represents the current instance of the class, so the key is to determine whether the instance of the class is an unenhanced XService (hereinafter referred to as XService), or an XService enhanced by CGLIB (hereinafter referred to as XServiceCglib).
insert image description here
In Test, the instance variable of the XService class is a Bean managed by the Spring framework. When executing test(), it will be injected according to the @Autowired annotation, so the instance of XService is actually XServiceCglib instead of XService.

When XServiceCglib.doOutside() is executed, since the subclass does not override the method with the same name of the parent class, it actually executes the doOutside() method of the parent class XService, so when executing its this.doInside(), it actually calls the parent The class does not enhance doInside(), so the transaction manager fails.

This problem exists widely in Spring AOP, that is, self-invocation, which is essentially a blind spot that dynamic proxies cannot solve. Only static proxies such as AspectJ can solve it.

Solution

Method 1: 1. It is best to call its method outside the proxy class

Method 2: 2. Self-injection (Self Injection, from Spring 4.3)

@Controller
class XService {
    
    
    @Autowired
    private YService yService;
    @Autowired
    private XService xService;
    public void doOutside(){
    
    
        xService.doInside();//从this换成了xService
    }
    @Transactional
    private void doInside(){
    
    
        //do sql statement
    }
}
@Controller
class Test {
    
    
    @Autowired
    private XService xService;
    public void test(){
    
    
        xService.doOutside();
    }
}

Since the xService variable is injected by Spring, it actually points to the XService$$Cglib object, so xService.doInside() can also correctly point to the enhanced method.

Not just transaction notifications, all AOP notifications and internal calls you implement using Spring will be subject to the same restrictions

Guess you like

Origin blog.csdn.net/LOVE_sel/article/details/106205081