SpringBoot使用AOP,內部方法失效的解決方案

SpringBoot使用AOP,內部方法失效

最近在使用AOP的時候,發現一個問題,普通的方法AOP就能夠有用,而內部調用的方法AOP就會失效,下面重現下問題

AOP切面

@Aspect
@Component
public class AuthorityAspect {
    @Pointcut("execution(* authority.service.AuthorityService.getExecutableSql(..))")
    private void pointCut() {}
    @Around(value = "pointCut()")
    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        String sql = (String) args[1];
        args[1] = sql + " AOP ";
        return joinPoint.proceed(args);
    }

該切面的作用就是,當執行getExecutableSql()方法,織入切面,獲取到該方法的參數,修改參數然後再執行該方法

現在有兩個方法

@Service
public class AuthorityService {
    public String getExecutableSql(String dataSourceId, String sql, Object page){
        return sql;
    }
    public String getInner(String dataSourceId, String sql, Object page){
        String str = this.getExecutableSql(dataSourceId, sql, page);
        return str;
    }
}
  • 一個是普通方法getExecutableSql(),
  • 一個是在內部調用瞭getExecutableSql()的getInner()方法

同時運行這兩個方法

在這裡插入圖片描述

可以發現內部調用瞭getExecutableSql()的方法,AOP對它並沒有生效,直接調用的就生效瞭。在說原因前,我們先說說動態代理 因為AOP的原理就是動態代理

動態代理簡單來說就是在程序運行的過程中,自動生成一個指定的代理類,然後執行程序,這個代理類可以在破壞原來被代理對象的情況下做一下額外的工作。

寫一個簡單的動態代理的例子

//接口
public interface ProxyService {
    void save();
    void email();
    void register();
}
//實現類
@Service
public class ProxyServiceImpl implements ProxyService{
    @Override
    public void save() {
    }
    @Override
    public void email() {
    }
    @Override
    public void register() {
        this.save();
        this.email();
    }
}
//獲得代理
public class ServiceProxy {
    public static Object getProxy(Class<?> clazz,Object target){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(target, args);
                System.out.println("after method: "+method.getName());
                return invoke;
            }
        });
    }
}
//測試類
public class ProxyTest {
    public static void main(String[] args) {
        ProxyService proxyService = (ProxyService) ServiceProxy.getProxy(ProxyService.class, new ProxyServiceImpl());
        proxyService.register();
    }
}

結果:

在這裡插入圖片描述

可以看到動態代理中,內部方法調用也是沒有生效,通過對代理類的Class文件分析,我們可以知道隻有通過代理類的實例執行的方法才會進入到攔截處理中

在這裡插入圖片描述

而動態代理的方法真實調用,會使用真實被代理對象實例進行方法調用,故在實例方法中通過this獲取的都是被代理的真實對象的實例,而不是代理對象自身。通過Debug可以看到

在這裡插入圖片描述

this獲取的不是代理對象,因此無法進入攔截處理,所以代理增強的方法沒有執行。原因知道瞭,那就解決辦法就是讓代理對象去調用這些方法,就能夠進入攔截處理瞭,修改下代碼

@Service
public class ProxyServiceImpl implements ProxyService{
    
    @Override
    public void save() {
    }
    @Override
    public void email() {
    }
    @Override
    public void register() {
        //獲取代理對象
        ProxyService proxyService = (ProxyService) ServiceProxy.getProxy(ProxyService.class, new ProxyServiceImpl());
        proxyService.save();
        proxyService.email();
    }
}

執行一下

在這裡插入圖片描述

在SpringBoot中可以用下面的方法,來使內部調用的方法AOP也能對其生效

@Service
public class AuthorityService {
	//獲取代理對象
    @Autowired
    private AuthorityService authorityService;
    public String getExecutableSql(String dataSourceId, String sql, Object page){
        return sql;
    }
    public String getInner(String dataSourceId, String sql, Object page){
        String str = authorityService.getExecutableSql(dataSourceId, sql, page);
        return str;
    }
}

在這裡插入圖片描述

可以看到內部調用的方法也生效瞭

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: