Spring 注释 AOP 调用了两次
Spring annotation AOP called twice
我用自定义注释注释我的 spring 引导控制器的一些功能以用于日志记录目的。但是,我发现之前的建议对于嵌套方法执行了两次。在这里寻找一些想法。请参考下面的代码片段。
控制器
@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());
}
}
在controller中触发apply函数时,会打印如下日志。
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$eeb128.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$eeb128.invoke(int, Object, Object[]) line: not available
我 运行 不明白为什么第 593 行会触发 doBefore。同样的情况也适用于 test1 的打印。
我的项目没有任何XML配置,所有配置都在注释中完成。
感谢您显示我在评论中要求的日志输出。现在我可以告诉你问题是什么:
Before ..........call(String com.mycomp.controller.InstallmentController.test())
Before ..........execution(String com.mycomp.controller.InstallmentController.test())
很明显你使用的是 AspectJ(可能与 LTW 一起使用),而不是 Spring AOP。为什么我可以这么说?因为 Spring AOP 只知道 execution()
个连接点,不知道 call()
个连接点。
由于上面给出的原因,您的切入点为每个方法调用匹配两次:一次用于进行调用的连接点(调用者),一次用于实际执行被调用方法的连接点(被叫方)。这就是为什么您在日志中得到两条输出行的原因。
所以你真正想要做的是在你的切入点中指定你想要拦截、调用或执行的内容。我建议您将 && execution(* *(..))
添加到切入点。然后你会得到预期的结果,类似于 Spring AOP 即使没有添加也会做的事情。
经验教训:AspectJ 比 Spring AOP 强大得多。您需要学习如何使用如此强大的工具,有时还要故意限制它的威力。 :-)
我用自定义注释注释我的 spring 引导控制器的一些功能以用于日志记录目的。但是,我发现之前的建议对于嵌套方法执行了两次。在这里寻找一些想法。请参考下面的代码片段。
控制器
@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());
}
}
在controller中触发apply函数时,会打印如下日志。
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$eeb128.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$eeb128.invoke(int, Object, Object[]) line: not available
我 运行 不明白为什么第 593 行会触发 doBefore。同样的情况也适用于 test1 的打印。
我的项目没有任何XML配置,所有配置都在注释中完成。
感谢您显示我在评论中要求的日志输出。现在我可以告诉你问题是什么:
Before ..........call(String com.mycomp.controller.InstallmentController.test())
Before ..........execution(String com.mycomp.controller.InstallmentController.test())
很明显你使用的是 AspectJ(可能与 LTW 一起使用),而不是 Spring AOP。为什么我可以这么说?因为 Spring AOP 只知道
execution()
个连接点,不知道call()
个连接点。由于上面给出的原因,您的切入点为每个方法调用匹配两次:一次用于进行调用的连接点(调用者),一次用于实际执行被调用方法的连接点(被叫方)。这就是为什么您在日志中得到两条输出行的原因。
所以你真正想要做的是在你的切入点中指定你想要拦截、调用或执行的内容。我建议您将
&& execution(* *(..))
添加到切入点。然后你会得到预期的结果,类似于 Spring AOP 即使没有添加也会做的事情。
经验教训:AspectJ 比 Spring AOP 强大得多。您需要学习如何使用如此强大的工具,有时还要故意限制它的威力。 :-)