解決spring AOP中自身方法調用無法應用代理的問題
spring AOP中自身方法調用無法應用代理
如下例
public class MyServiceImpl implements MyService { public void do(){ //the transaction annotation won't work if you directly invoke handle() method with 'this' this.handle(); } @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void handle() { //sth with transaction } }
如果直接調用this的handle()方法則事務無法生效,原因是spring的AOP是通過代理實現的,像這樣直接調用本對象的方法是不會應用代理的。
可以使用如下兩種方式修改代碼以應用事務
(1)在MyServiceImpl中聲明一個MyService對象
public class MyServiceImpl implements MyService { @Autowired private MyService myService; public void do(){ //use myService object myService.handle(); } @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void handle() { //sth. with transaction } }
(2)使用AopContext類
public class MyServiceImpl implements MyService { public void do(){ //fetch current proxy objet from AopContext ((MyService)AopContext.currentProxy()).handle(); } @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void handle() { //sth with transaction } }
註意,原生的AspectJ是不會有這種自身調用的問題的,因為它不是基於代理的AOP框架。
spring aop 內部方法調用事務不生效
方法1:
基於 proxy 的 spring aop 帶來的內部調用問題可以使用 AopContext.currentProxy() 強轉為當前的再調用就可以解決瞭
例如:
錯誤用法:
public Account getAccountByName2(String userName) { return this.getAccountByName(userName); }
修改為:
public Account getAccountByName2(String userName) { return ((AccountService)AopContext.currentProxy()).getAccountByName(userName); }
另外註意:要設置aop實體暴露出來。在springboot的application.java裡面加上
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
方法2:
利用初始化方法在目標對象中註入代理對象
在目標對象類中註入spring上下文,通過context獲取代理對象,並調用代理對象的方法。
註意:該方案對於scope為prototype的bean無法適用,因為每次獲取bean時都返回一個新的對象。
方法2.1:
//延遲加載方式 private TestService testService; @Autowired @Lazy public void setTestService(TestService testService) { this.testService = testService; }
方法2.2:
import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import com.blog.common.aop.service.TestService; @Service public class TestServiceImpl implements TestService { @Autowired private ApplicationContext context; private TestService proxyObject; @PostConstruct // 初始化方法,在IOC註入完成後會執行該方法 private void setSelf() { // 從spring上下文獲取代理對象(直接通過proxyObject=this是不對的,this是目標對象) // 此種方法不適合於prototype Bean,因為每次getBean返回一個新的Bean proxyObject = context.getBean(TestService.class); } public void methodA() throws Exception { System.out.println("method A run"); System.out.println("method A 中調用method B,通過註入的代理對象,調用代理對象的方法,解決內部調用實現的問題。"); proxyObject.methodB(); //調用代理對象的方法,解決內部調用失效的問題 } public void methodB() { System.out.println("method B run"); } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 老生常談spring的事務傳播機制
- Spring詳細講解事務失效的場景
- 面試JAVA時,問到spring該怎麼回答
- 詳解Java如何使用註解來配置Spring容器
- SpringBoot在自定義類中調用service層等Spring其他層操作