SpringBoot使用AOP,內(nèi)部方法失效的解決方案
SpringBoot使用AOP,內(nèi)部方法失效
最近在使用AOP的時(shí)候,發(fā)現(xiàn)一個(gè)問題,普通的方法AOP就能夠有用,而內(nèi)部調(diào)用的方法AOP就會(huì)失效,下面重現(xiàn)下問題
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);
}
該切面的作用就是,當(dāng)執(zhí)行g(shù)etExecutableSql()方法,織入切面,獲取到該方法的參數(shù),修改參數(shù)然后再執(zhí)行該方法
現(xiàn)在有兩個(gè)方法
@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;
}
}
- 一個(gè)是普通方法getExecutableSql(),
- 一個(gè)是在內(nèi)部調(diào)用了getExecutableSql()的getInner()方法
同時(shí)運(yùn)行這兩個(gè)方法

可以發(fā)現(xiàn)內(nèi)部調(diào)用了getExecutableSql()的方法,AOP對(duì)它并沒有生效,直接調(diào)用的就生效了。在說原因前,我們先說說動(dòng)態(tài)代理 因?yàn)锳OP的原理就是動(dòng)態(tài)代理
動(dòng)態(tài)代理簡(jiǎn)單來(lái)說就是在程序運(yùn)行的過程中,自動(dòng)生成一個(gè)指定的代理類,然后執(zhí)行程序,這個(gè)代理類可以在破壞原來(lái)被代理對(duì)象的情況下做一下額外的工作。
寫一個(gè)簡(jiǎn)單的動(dòng)態(tài)代理的例子
//接口
public interface ProxyService {
void save();
void email();
void register();
}
//實(shí)現(xiàn)類
@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;
}
});
}
}
//測(cè)試類
public class ProxyTest {
public static void main(String[] args) {
ProxyService proxyService = (ProxyService) ServiceProxy.getProxy(ProxyService.class, new ProxyServiceImpl());
proxyService.register();
}
}
結(jié)果:

可以看到動(dòng)態(tài)代理中,內(nèi)部方法調(diào)用也是沒有生效,通過對(duì)代理類的Class文件分析,我們可以知道只有通過代理類的實(shí)例執(zhí)行的方法才會(huì)進(jìn)入到攔截處理中

而動(dòng)態(tài)代理的方法真實(shí)調(diào)用,會(huì)使用真實(shí)被代理對(duì)象實(shí)例進(jìn)行方法調(diào)用,故在實(shí)例方法中通過this獲取的都是被代理的真實(shí)對(duì)象的實(shí)例,而不是代理對(duì)象自身。通過Debug可以看到

this獲取的不是代理對(duì)象,因此無(wú)法進(jìn)入攔截處理,所以代理增強(qiáng)的方法沒有執(zhí)行。原因知道了,那就解決辦法就是讓代理對(duì)象去調(diào)用這些方法,就能夠進(jìn)入攔截處理了,修改下代碼
@Service
public class ProxyServiceImpl implements ProxyService{
@Override
public void save() {
}
@Override
public void email() {
}
@Override
public void register() {
//獲取代理對(duì)象
ProxyService proxyService = (ProxyService) ServiceProxy.getProxy(ProxyService.class, new ProxyServiceImpl());
proxyService.save();
proxyService.email();
}
}
執(zhí)行一下

在SpringBoot中可以用下面的方法,來(lái)使內(nèi)部調(diào)用的方法AOP也能對(duì)其生效
@Service
public class AuthorityService {
//獲取代理對(duì)象
@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;
}
}

可以看到內(nèi)部調(diào)用的方法也生效了
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- springboot使用自定義注解實(shí)現(xiàn)aop切面日志
- SpringBoot使用AOP記錄接口操作日志詳解
- SpringBoot使用AOP實(shí)現(xiàn)統(tǒng)計(jì)全局接口訪問次數(shù)詳解
- 在springboot中使用AOP進(jìn)行全局日志記錄
- Springboot使用@Valid 和AOP做參數(shù)校驗(yàn)及日志輸出問題
- 詳解基于SpringBoot使用AOP技術(shù)實(shí)現(xiàn)操作日志管理
- SpringBoot使用AOP+注解實(shí)現(xiàn)簡(jiǎn)單的權(quán)限驗(yàn)證的方法
- SpringBoot中使用AOP打印接口日志的方法
- SpringBoot項(xiàng)目中使用AOP的方法
- Springboot 中使用 Aop代碼實(shí)戰(zhàn)教程
相關(guān)文章
簡(jiǎn)單的理解java集合中的HashSet和HashTree幾個(gè)重寫方法
這篇文章主要介紹了簡(jiǎn)單的理解java集合中的HashSet和HashTree幾個(gè)重寫方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Java 線程的生命周期詳細(xì)介紹及實(shí)例代碼
這篇文章主要介紹了Java 線程的生命周期的相關(guān)資料,并附簡(jiǎn)單實(shí)例代碼,幫助大家理解,需要的朋友可以參考下2016-10-10
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(52)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧,希望可以幫到你2021-08-08
IDEA 2020.1 搜索不到Chinese (Simplified) Language
小編在安裝中文插件時(shí)遇到IDEA 2020.1 搜索不到Chinese ​(Simplified)​ Language Pack EAP,無(wú)法安裝的問題,本文給大家分享我的解決方法,感興趣的朋友一起看看吧2020-04-04
SpringBoot使用knife4j進(jìn)行在線接口調(diào)試
這篇文章主要介紹了SpringBoot使用knife4j進(jìn)行在線接口調(diào)試,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
commons fileupload實(shí)現(xiàn)文件上傳的實(shí)例代碼
這篇文章主要介紹了commons fileupload實(shí)現(xiàn)文件上傳的實(shí)例代碼,包括文件上傳的原理分析等相關(guān)知識(shí)點(diǎn),本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-10-10

