Spring 注解 AOP 调用两次 [英] Spring annotation AOP called twice
问题描述
我使用自定义注释为我的 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())
很明显,您使用的是 AspectJ(可能使用 LTW),而不是 Spring AOP.为什么我可以这么说?因为 Spring AOP 只知道
execution()
连接点,而不知道call()
连接点.
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, notcall()
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屋!