解決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。

推薦閱讀: