javascript
Spring —— 基于注解的Aop在同一类下产生嵌套时切面不生效问题产生原因及解决
一、背景介紹
由于程序中大量方法需要監(jiān)控執(zhí)行耗時(shí),因此寫(xiě)了基于注解的Aop類來(lái)減少重復(fù)代碼,主要作用是通過(guò)環(huán)繞通知在方法執(zhí)行前后進(jìn)行耗時(shí)計(jì)算,最后輸出到日志/監(jiān)控。
相關(guān)代碼如下:
// 注解 @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD}) public @interface AddExecTime{String processName();}/*** 用于記錄各過(guò)程耗時(shí)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)用完成,耗時(shí){}ms",addExecTimeToQTrace.processName(),stopWatch.getTime());return result;}} }二、問(wèn)題描述
預(yù)期效果是從Controller層開(kāi)始添加方法,然后對(duì)其調(diào)用的核心方法加上耗時(shí)監(jiān)控注解,每個(gè)方法最后都應(yīng)該輸出相關(guān)耗時(shí)到日志/監(jiān)控。
然而,在實(shí)際調(diào)用過(guò)程中卻發(fā)現(xiàn),只有Controller層注解輸出了耗時(shí)及Service層第一個(gè)注解輸出了耗時(shí)。這是為什么???
相關(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切面的原理是對(duì)目標(biāo)類生成一個(gè)代理對(duì)象,然后對(duì)代理對(duì)象進(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() {//后置通知邏輯} }乍一看貌似沒(méi)問(wèn)題,但是要注意的是,代理類使用的是目標(biāo)類的方法,即WebService的方法,而非WebServiceProxy。因此代理類調(diào)用a方法時(shí),其a方法接下來(lái)調(diào)用的是webService中不帶前置/后置通知的b方法,之后又同理。
所以,問(wèn)題總結(jié)為同一個(gè)類出現(xiàn)切面注解嵌套時(shí),并不總是由代理類調(diào)用代理方法,導(dǎo)致切面注解嵌套后只有第一個(gè)被調(diào)用的方法能夠執(zhí)行前/后置通知。
四、解決方法
每個(gè)方法在調(diào)用下一層方法時(shí),都調(diào)用其代理類的代理方法去執(zhí)行。需要注意的是,這種做法如果獲取不到代理類就會(huì)導(dǎo)致方法調(diào)用失敗,因此需要使得代理對(duì)象能夠被獲取。
那么如何使用代理對(duì)象可獲得?
接下里修改實(shí)際調(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在同一类下产生嵌套时切面不生效问题产生原因及解决的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 应用matlab仿真几类混沌电路,典型混
- 下一篇: 最深的红尘里,陌上为谁花开