分析java中AspectJ切面执行两次的原因

  

<>强分析java中AspectJ切面执行两次的原因

  

<强>背景
  

  

转眼之间,发现博客已经将近半年没更新了,甚是惭愧。话不多说,正如标题所言,最近在使用AspectJ的时候,发现拦截器(AOP切面)执行了两次了。我们知道,AspectJ是AOP的一种解决方案,本质上是通过代理类在目标方法执行通知(建议),然后由代理类再去调用目标方法,所以,从这点讲,拦截器应该只会执行一次。但是在测试的时候发现拦截器执行了两次。

  

<强>问题重现
  

  

既然问题已经明了,那么可以通过代码简单重现这个问题,从而更深层次分析到底是什么原因导致的。

  

定义一个注解:

        包com.rhwayfun.aspect;      进口java.lang.annotation。*;      @Target ({ElementType.METHOD})   @Retention (RetentionPolicy.CLASS)   @Documented   公共@ interface StatsService {   }      之前      

为该注解定义切面:

        包com.rhwayfun.aspect;      进口org.aspectj.lang.ProceedingJoinPoint;   进口org.aspectj.lang.annotation.Around;   进口org.aspectj.lang.annotation.Aspect;   进口org.slf4j.Logger;   进口org.slf4j.LoggerFactory;      @Aspect   公开课StatsServiceInterceptor {      私有静态日志记录器=LoggerFactory.getLogger (StatsServiceInterceptor.class);      @Around (“@annotation (StatsService)”)   公共对象调用(ProceedingJoinPoint pjp) {   尝试{   log.info(“之前调用目标。”);   返回pjp.proceed ();   }捕捉(Throwable e) {   日志。错误(“调用发生错误:e);   返回null;   最后}{   log.info(“后调用目标。”);   }   }      }      之前      

<强>方法测试:

        包com.rhwayfun;      进口com.rhwayfun.aspect.StatsService;   进口org.slf4j.Logger;   进口org.slf4j.LoggerFactory;      进口java.time.LocalDateTime;      公开课方面{      私有静态日志记录器=LoggerFactory.getLogger (AspectTest.class);      公共静态void main (String [] args) {   AspectTest.print ();   }      @StatsService   公共静态孔隙print () {   log.info(“现在:{}”,LocalDateTime.now ());   }   }      之前      

输出结果:

  

分析java中AspectJ切面执行两次的原因

  

<强>调试分析
  

  

由于是静态织入,所以可以通过反编译工具查看编译后的文件,如下:

        公开课方面   {   私有静态日志记录器;   私有静态最终合成*/*/ajc StaticPart tjp_0美元的连接点;   私有静态最终合成*/*/ajc StaticPart tjp_1美元的连接点;      最后公共静态void main (String [] args) {   StatsServiceInterceptor.aspectOf () .invoke (((AroundClosure)新方面AjcClosure1美元(新对象[]{Factory.makeJP(方面。ajc tjp_0美元(对象)空(对象)零)})).linkClosureAndJoinPoint (0));   }      @StatsService   公共静态孔隙print () {   StatsServiceInterceptor.aspectOf () .invoke (((AroundClosure)新方面AjcClosure3美元(新对象[]{Factory.makeJP(方面。ajc tjp_1美元(对象)空(对象)零)})).linkClosureAndJoinPoint (65536));   }      静态{   ajc preClinit美元();   方面。日志=LoggerFactory.getLogger((类)AspectTest.class);   }      私有静态/*合成*/空白ajc preClinit美元(){   最终工厂工厂=新工厂(”方面。java”(类)AspectTest.class);   ajc $ tjp_0=工厂。makeSJP(“方法”(签名)工厂。makeMethodSig(“9”、“打印”、“com.rhwayfun。方面”、“”、“”、“”、“空白”),17);   ajc $ tjp_1=工厂。makeSJP(“方法执行”(签名)工厂。makeMethodSig(“9”、“打印”、“com.rhwayfun。方面”、“”、“”、“”、“空白”),22);   }   }      之前      

请注意两个连接点:ajc tjp_0美元和ajc tjp_1美元,这两个连接点是产生两次调用的关键,问题注解明明是加上打印()方法上的,为什么主要()方法也被注入了通知呢?正因为主要()方法也织入了通知,所以就形成了调用B, B调用print()的调用链,有两次方法调用,一次方法执行,方法执行才是我们的目标方法打印(),所以我们才看到了两次输出。

     

分析java中AspectJ切面执行两次的原因