Spring annotation AOP called twice

自作多情 提交于 2021-02-18 12:57:17

问题


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.

Controller

@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";
}

The annotation

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

}

The Aspect

@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

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   

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

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


回答1:


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. 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.

  2. 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.

  3. 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.

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. :-)



来源:https://stackoverflow.com/questions/48683175/spring-annotation-aop-called-twice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!