【问题标题】:Spring annotation AOP called twiceSpring注解AOP调用了两次
【发布时间】:2018-02-08 10:26:00
【问题描述】:

我使用自定义注释对我的 Spring Boot 控制器的一些功能进行注释以用于日志记录。但是,我发现嵌套方法执行了两次之前的建议。在这里寻找一些想法。请参考下面的代码sn-ps。

控制器

@RequestMapping(value = "apply")
    @OperationMILog
    public ApplyHttpResponse apply(@RequestHeader final String custId, @RequestAttribute final String cardNo,
        @RequestBody final InstallmentApplyHttpRequest installApplyReq, @PathVariable final String source) {

        //test
        this.test();  //**line 387**

...
}


 ....
     @OperationMILog
        private String test() {
            return this.test1(); //**line 593**
        }

@OperationMILog
private String test1() {
    return "test1";
}

注释

@Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface OperationMILog {

}

方面

@Aspect
public class GenericLoggingAspect {

      public static GenericLoggingAspect genericLoggingAspect;

        @PostConstruct
        public void init() {    
            genericLoggingAspect = this;
        }


    @Before("@annotation(com.mycomp.log.OperationMILog)")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("Before .........." + joinPoint.getSignature().getName());
    }

}

当触发控制器中的应用功能时,会打印以下日志。

Before ..........apply
Before ..........test
Before ..........test
Before ..........test1
Before ..........test1

在 doBefore 函数处设置断点并在调试模式下查看堆栈跟踪。 打印第一个“Before .......... test”时,堆栈跟踪看起来很好。

GenericLoggingAspect.doBefore(JoinPoint) line: 87   
InstallmentController.apply(String, String, InstallmentApplyHttpRequest, String) line: 387  
InstallmentController$$FastClassBySpringCGLIB$$55eeb128.invoke(int, Object, Object[]) line: not available   

当第二个“Before .......... test”要显示时,堆栈跟踪如下所示

GenericLoggingAspect.doBefore(JoinPoint) line: 87   
InstallmentController.test() line: 593  
InstallmentController.apply(String, String, InstallmentApplyHttpRequest, String) line: 387  
InstallmentController$$FastClassBySpringCGLIB$$55eeb128.invoke(int, Object, Object[]) line: not available   

我不知道为什么第 593 行会触发 doBefore。同样的情况也适用于 test1 的打印。

我的项目没有任何 XML 配置,所有配置都是在注解中完成的。

【问题讨论】:

  • 请记录完整的joinPoint,而不是仅记录目标方法名称。我想查看日志并检查一些内容。谢谢。
  • @kriegaex .关于这两个“Before......test”,jointPoint的完整日志是:............call(String com.mycomp .controller.InstallmentController.test()) 之前 ..........execution(String com.mycomp.controller.InstallmentController.test())

标签: annotations aop spring-aop


【解决方案1】:

感谢您在评论中显示我要求的日志输出。现在我可以告诉你问题出在哪里了:

Before ..........call(String com.mycomp.controller.InstallmentController.test())
Before ..........execution(String com.mycomp.controller.InstallmentController.test())
  1. 很明显,您使用的是 AspectJ(可能与 LTW 一起使用),而不是 Spring AOP。为什么我可以这么说?因为 Spring AOP 只知道 execution() 连接点,而不知道 call() 连接点。

  2. 由于上述原因,每个方法调用的切入点匹配两次:一次用于进行调用的连接点(调用者),一次用于实际执行被调用方法的连接点(被调用者)。这就是为什么您会在日志中获得两个输出行。

  3. 所以你真正想要做的是在你的切入点中指定你想要拦截、调用或执行的确切内容。我建议您将&& execution(* *(..)) 添加到切入点。然后你得到预期的结果,类似于 Spring AOP 即使没有添加也会做的事情。

经验教训:AspectJ 比 Spring AOP 强大得多。您需要学习如何使用如此强大的工具,并且有时会故意限制其功能。 :-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多