Spring 注解 AOP 调用两次 [英] Spring annotation AOP called twice

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

问题描述

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

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

}

在控制器中触发apply函数时,打印如下日志.

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

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

在 doBefore 函数处设置断点并在调试模式下查看堆栈跟踪.打印第一个Before …… test"时,堆栈跟踪看起来不错.

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   

当第二个Before .......... test"将显示时,堆栈跟踪非常连贯如下

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.

所以你真正想要做的是在你的切入点中指定你到底想要拦截、调用或执行什么.我建议你添加 &&execution(* *(..)) 到切入点.然后你会得到预期的结果,类似于 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 调用两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆