SpringBoot使用AOP,內(nèi)部方法失效的解決方案
SpringBoot使用AOP,內(nèi)部方法失效
最近在使用AOP的時(shí)候,發(fā)現(xiàn)一個(gè)問(wèn)題,普通的方法AOP就能夠有用,而內(nèi)部調(diào)用的方法AOP就會(huì)失效,下面重現(xiàn)下問(wè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ì)它并沒(méi)有生效,直接調(diào)用的就生效了。在說(shuō)原因前,我們先說(shuō)說(shuō)動(dòng)態(tài)代理 因?yàn)锳OP的原理就是動(dòng)態(tài)代理
動(dòng)態(tài)代理簡(jiǎn)單來(lái)說(shuō)就是在程序運(yùn)行的過(guò)程中,自動(dòng)生成一個(gè)指定的代理類(lèi),然后執(zhí)行程序,這個(gè)代理類(lèi)可以在破壞原來(lái)被代理對(duì)象的情況下做一下額外的工作。
寫(xiě)一個(gè)簡(jiǎn)單的動(dòng)態(tài)代理的例子
//接口 public interface ProxyService { void save(); void email(); void register(); } //實(shí)現(xiàn)類(lèi) @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è)試類(lèi) 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)用也是沒(méi)有生效,通過(guò)對(duì)代理類(lèi)的Class文件分析,我們可以知道只有通過(guò)代理類(lèi)的實(shí)例執(zhí)行的方法才會(huì)進(jìn)入到攔截處理中
而動(dòng)態(tài)代理的方法真實(shí)調(diào)用,會(huì)使用真實(shí)被代理對(duì)象實(shí)例進(jìn)行方法調(diào)用,故在實(shí)例方法中通過(guò)this獲取的都是被代理的真實(shí)對(duì)象的實(shí)例,而不是代理對(duì)象自身。通過(guò)Debug可以看到
this獲取的不是代理對(duì)象,因此無(wú)法進(jìn)入攔截處理,所以代理增強(qiáng)的方法沒(méi)有執(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ì)全局接口訪問(wèn)次數(shù)詳解
- 在springboot中使用AOP進(jìn)行全局日志記錄
- Springboot使用@Valid 和AOP做參數(shù)校驗(yàn)及日志輸出問(wè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)文章
如何搭建一個(gè)完整的Java開(kāi)發(fā)環(huán)境
這篇文章主要教大家如何搭建一個(gè)完整的Java開(kāi)發(fā)環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11SpringCloud Stream消息驅(qū)動(dòng)實(shí)例詳解
這篇文章主要介紹了SpringCloud Stream消息驅(qū)動(dòng)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03MyBatis執(zhí)行批處理操作的實(shí)現(xiàn)示例
在MyBatis中,批處理操作是一種高效執(zhí)行多條語(yǔ)句的方式,下面就來(lái)介紹一下MyBatis執(zhí)行批處理操作的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07idea 實(shí)現(xiàn)搜索jdk中的類(lèi)和包操作
這篇文章主要介紹了idea 實(shí)現(xiàn)搜索jdk中的類(lèi)和包操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02使用maven的profile構(gòu)建不同環(huán)境配置的方法
這篇文章主要介紹了使用maven的profile構(gòu)建不同環(huán)境配置的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01源碼解析springbatch的job運(yùn)行機(jī)制
這篇文章主要介紹了springbatch的job是如何運(yùn)行的,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08