繁体   English   中英

将 AOP 添加到 Spring 引导过程将方法返回的 object 更改为 null

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM