[英]Adding AOP to Spring Boot process changes object returned by method to null
我想将用于日志记录的 AOP 添加到我的 Spring 引导应用程序中。 但它似乎以意想不到的方式改变了我的应用程序的行为。
例如,我的应用程序有一个方法doThis()
,它创建了一个MyObject
实例:
MyObject myObect = doThis(); // a non-null myObject is returned from doThis()
这很好用,并且myObject
按预期填充了从doThis()
返回的实例。 但我希望doThis()
方法也可以通过 AOP 记录一些消息。
那么我添加以下方面 class:
@Aspect
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
@Around("execution(* my.service.package.*.*(..))")
public void log(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("before");
joinPoint.proceed();
logger.info("after");
}
}
而且我还添加了这个配置class:
@Configuration
@ComponentScan(basePackages = "my.service.package")
@EnableAspectJAutoProxy
public class AppConfig {
@Bean
public LoggingAspect aspect() {
return new LoggingAspect();
}
}
然而,现在当我运行我的应用程序时,日志语句确实出现了,正如预期的那样——但现在同样的doThis()
方法显然返回了 null object:
MyObject myObect = doThis(); // myObject is now unexplainedly null
但这不是真的! 我的意思是当我在doThis()
的最后一行设置断点时,它即将返回的 MyObject 实例显然不是 null 。 它已在doThis()
方法中创建和填充。 那么它在哪里 go 呢? 当doThis()
清楚地返回一个非空的 MyObject 实例时,为什么myObject
没有被填充?
似乎这个方面以某种方式使从doThis()
返回的 object 无效。 有没有人见过这个? 有什么办法吗?
我相信我的执行语句中的第一个*
应该表明被拦截的方法可以有任何返回类型。 但是,我截获的方法的返回值似乎以某种方式更改为 null。
我正在研究如何根据评论创建一个“最小的可重现示例”,我将在此处添加该评论,但这似乎是一个相当标准的 AOP 用例,因此在此期间将其扔掉以防有人可能有一些见识。
您犯了一个简单的初学者 AOP 错误:您的@Around
建议继续执行,但不返回procedure proceed()
调用的结果。 你的通知方法有一个 void 返回类型,你拦截的目标方法没有。 因此,建议隐式返回null
。 顺便说一句,对于像int
这样的原始类型,由于返回类型不兼容,这甚至不起作用并引发异常。 实际上,我很惊讶 Spring AOP,奇怪的是,如果环绕建议返回 void,它甚至会拦截非 void 方法,因为在这种情况下,AFAIR 原生 AspectJ 不会匹配非 void 方法。
所以,你可以做什么?
如果您真的认为需要,请保留@Around
建议。 通常,仅当您想做的不仅仅是记录事情时,例如修改方法参数或返回值、处理异常或其他可能改变控制流的事情时,才会出现这种情况:
@Around("execution(* my.service.package.*.*(..))") public Object log(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("[BEFORE] " + joinPoint); try { return joinPoint.proceed(); } finally { logger.info("[AFTER] " + joinPoint); } }
或者保持简单,只使用一对@Before
和@After
通知方法,如果不需要将数据从一个通知传输到另一个通知。 这要简单得多,因为您不需要继续,使用try-finally
或返回任何内容:
@Before("execution(* my.service.package.*.*(..))") public void logBefore(JoinPoint joinPoint) { logger.info("[BEFORE] " + joinPoint); } @After("execution(* my.service.package.*.*(..))") public void logAfter(JoinPoint joinPoint) { logger.info("[AFTER] " + joinPoint); }
在这里,您还可以将重复的切入点表达式分解到它自己的@Pointcut
中,并从两个通知方法中简单地引用它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.