javascript
Spring —— 基于注解的Aop在同一类下产生嵌套时切面不生效问题产生原因及解决
一、背景介紹
由于程序中大量方法需要監(jiān)控執(zhí)行耗時,因此寫了基于注解的Aop類來減少重復(fù)代碼,主要作用是通過環(huán)繞通知在方法執(zhí)行前后進(jìn)行耗時計算,最后輸出到日志/監(jiān)控。
相關(guān)代碼如下:
// 注解 @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD}) public @interface AddExecTime{String processName();}/*** 用于記錄各過程耗時ms的Aop類*/ @Slf4j @Aspect @Component public class CostTimeAop {@Pointcut(value = "@annotation(com.demo.web.annotation.AddExecTime)")public void addTimelineMsg(){}@Around(value = "addTimelineMsg()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();AddExecTime addExecTime = method.getAnnotation(AddExecTime.class);StopWatch stopWatch = new StopWatch();Object result = null;try {log.info("Process {} ,add timeline msg",addExecTime.processName());stopWatch.start();result = joinPoint.proceed();}catch (Exception e){log.error("CostTimeAop around error",e);}finally {stopWatch.stop();log.info("{}方法調(diào)用完成,耗時{}ms",addExecTimeToQTrace.processName(),stopWatch.getTime());return result;}} }二、問題描述
預(yù)期效果是從Controller層開始添加方法,然后對其調(diào)用的核心方法加上耗時監(jiān)控注解,每個方法最后都應(yīng)該輸出相關(guān)耗時到日志/監(jiān)控。
然而,在實際調(diào)用過程中卻發(fā)現(xiàn),只有Controller層注解輸出了耗時及Service層第一個注解輸出了耗時。這是為什么???
相關(guān)代碼如下:
//Controller層 @RestController public WebController{@Autowiredprivate WebService webservice;@AddExecTime(processName = "Controller層getResult方法")public String getResult(){return webservice.a();} }// Service層 @Service public WebService{@AddExecTime(processName = "Service層a方法")public String a(){return webservice.b();}@AddExecTime(processName = "Service層b方法")public String b(){return webservice.c();}@AddExecTime(processName = "Service層c方法")public String c(){return "I am free!";} }三、查找原因
首先,AspectJ切面的原理是對目標(biāo)類生成一個代理對象,然后對代理對象進(jìn)行前置/后置/環(huán)繞操作,拿WebService做例子。
clase WebServiceProxy {private WebService webService;...public String a(){doBefore();webService.b();doAfter();}public String b() {doBefore();webService.c();doAfter();}public String c() {doBefore();// 執(zhí)行c方法邏輯doAfter();}public doBefore() {//前置通知邏輯}public doAfter() {//后置通知邏輯} }乍一看貌似沒問題,但是要注意的是,代理類使用的是目標(biāo)類的方法,即WebService的方法,而非WebServiceProxy。因此代理類調(diào)用a方法時,其a方法接下來調(diào)用的是webService中不帶前置/后置通知的b方法,之后又同理。
所以,問題總結(jié)為同一個類出現(xiàn)切面注解嵌套時,并不總是由代理類調(diào)用代理方法,導(dǎo)致切面注解嵌套后只有第一個被調(diào)用的方法能夠執(zhí)行前/后置通知。
四、解決方法
每個方法在調(diào)用下一層方法時,都調(diào)用其代理類的代理方法去執(zhí)行。需要注意的是,這種做法如果獲取不到代理類就會導(dǎo)致方法調(diào)用失敗,因此需要使得代理對象能夠被獲取。
那么如何使用代理對象可獲得?
接下里修改實際調(diào)用方法如下使用WebService類進(jìn)行演示:
@Service public WebService{@AddExecTime(processName = "Service層a方法")public String a(){return ((WebService)AopContext.currentProxy()).b();}@AddExecTime(processName = "Service層b方法")public String b(){return ((WebService)AopContext.currentProxy()).c();}@AddExecTime(processName = "Service層c方法")public String c(){return "I am free!";} }參考資料:
https://www.cnblogs.com/mzcx/p/11430846.html
https://www.cnblogs.com/chihirotan/p/7356683.html
https://blog.csdn.net/u013212754/article/details/103138486
https://www.jianshu.com/p/1e2f9168a6c7
總結(jié)
以上是生活随笔為你收集整理的Spring —— 基于注解的Aop在同一类下产生嵌套时切面不生效问题产生原因及解决的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 应用matlab仿真几类混沌电路,典型混
- 下一篇: gradle idea java ssm