Spring注解AOP调用了两次

Spring注解AOP调用了两次

本文介绍了Spring注解AOP调用了两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I annotate my spring boot controller some functions with a custom annotation for logging purpose. However, I find the before advice is executed twice for nested methods. Looking for some idea here. Please refer to the code snippets below.

控制器

@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());
    }

}

在控制器中触发应用功能时,将打印以下日志.

When triggering the apply function in controller, the following logs are printed.

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

在doBefore函数上设置断点,并在调试模式下查看堆栈跟踪.当打印出第一个"............测试之前"时,堆栈跟踪看起来很好.

Settin break point at doBefore function and Looking into the stack trace in debug mode .When the first "Before ............ test" is printed,the stack trace looks fine.

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

当第二个"..........测试之前"显示时,堆栈跟踪已如下所示接线

When the second "Before .......... test" is going to show, the stack trace is quite wired as below

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的打印.

I am running out of idea why line 593 triggers the doBefore. The same case applies to the printing of test1.

我的项目没有任何XML配置,所有配置都在注释中完成.

My project doesn't have any XML configuration, all configurations are done in annotations.

推荐答案

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

Thanks for showing the log output I asked for in my comment. Now I can tell you what the problem is:

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()连接点.

  1. It is obvious that you use AspectJ (probably with LTW), not Spring AOP. Why can I say that? Because Spring AOP only knows execution() joinpoints, not call() ones.

由于上述原因,您的切入点针对每个方法调用匹配两次:一次针对进行调用的连接点(调用方),一次针对实际执行被调用方法的连接点(被调用方).这就是为什么在日志中同时显示两条输出行的原因.

For the reason given above, your pointcut matches twice for each method call: once for the joinpoint where the call is made (caller) and once for the joinpoint where the called method is actually executed (callee). This is why you get both output lines in your log.

因此,您真正想要做的是在切入点中指定要截取,调用或执行的对象.我建议您添加&&执行(* *(..))到切入点.然后,您将获得预期的结果,类似于Spring AOP即使没有添加也将达到的结果.

So what you actually want to do is to specify in your pointcut what exactly you want to intercept, call or execution. I suggest you add && execution(* *(..)) to the pointcut. Then you get the expected result, similar to what Spring AOP would do even without that addition.

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

Lesson learned: AspectJ is much more powerful than Spring AOP. You need to learn how to wield such a powerful tool and sometimes limit its power on purpose. :-)

这篇关于Spring注解AOP调用了两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 03:32