简体   繁体   English

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

[英]Adding AOP to Spring Boot process changes object returned by method to null

I want to add AOP for logging purposes to my Spring Boot application.我想将用于日志记录的 AOP 添加到我的 Spring 引导应用程序中。 But it seems to change my application's behavior in unexpected ways.但它似乎以意想不到的方式改变了我的应用程序的行为。

For example, my application has a method, doThis() , which creates a MyObject instance:例如,我的应用程序有一个方法doThis() ,它创建了一个MyObject实例:

MyObject myObect = doThis();  // a non-null myObject is returned from doThis()

That works great and myObject is populated as expected with an instance returned from doThis() .这很好用,并且myObject按预期填充了从doThis()返回的实例。 But I want the doThis() method to also log some messages via AOP.但我希望doThis()方法可以通过 AOP 记录一些消息。

So then I add the following aspect class:那么我添加以下方面 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");
    }
}

And I also add this configuration class:而且我还添加了这个配置class:

@Configuration
@ComponentScan(basePackages = "my.service.package")
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public LoggingAspect aspect() {
        return new LoggingAspect();
    }
}

However, now when I run my app, the logging statements do appear, as expected - but now that same exact doThis() method apparently returns a null object:然而,现在当我运行我的应用程序时,日志语句确实出现了,正如预期的那样——但现在同样的doThis()方法显然返回了 null object:

MyObject myObect = doThis(); // myObject is now unexplainedly null

But that's not true!但这不是真的! What I mean by that is when I set a breakpoint on the last line of doThis() , the MyObject instance it is just about to return is very clearly not null .我的意思是当我在doThis()的最后一行设置断点时,它即将返回的 MyObject 实例显然不是 null It has been created and populated within the doThis() method.它已在doThis()方法中创建和填充。 So where did it go?那么它在哪里 go 呢? Why did myObject not get populated when doThis() clearly returned a non-null MyObject instance?doThis()清楚地返回一个非空的 MyObject 实例时,为什么myObject没有被填充?

It seems like the aspect is somehow nullifying the object returned from doThis() .似乎这个方面以某种方式使从doThis()返回的 object 无效。 Has anyone seen this before?有没有人见过这个? Is there any way around it?有什么办法吗?

I believe the first * in my execution statement is supposed to indicate that the intercepted method can have any return type.我相信我的执行语句中的第一个*应该表明被拦截的方法可以有任何返回类型。 But yet the returned value from my intercepted method seems to get somehow changed to null.但是,我截获的方法的返回值似乎以某种方式更改为 null。

I'm working on how I can create a "minimal reproducible example", per the comment, which I'll add here, but this seems like perhaps a fairly standard AOP use case, so throwing it out there in the meantime in case someone may have some insight.我正在研究如何根据评论创建一个“最小的可重现示例”,我将在此处添加该评论,但这似乎是一个相当标准的 AOP 用例,因此在此期间将其扔掉以防有人可能有一些见识。

You made a simple beginner's AOP mistake: Your @Around advice proceeds, but does not return the result of the proceed() call.您犯了一个简单的初学者 AOP 错误:您的@Around建议继续执行,但不返回procedure proceed()调用的结果。 Your advice method has a void return type, your intercepted target method has not.你的通知方法有一个 void 返回类型,你拦截的目标方法没有。 So the advice implicitly returns null .因此,建议隐式返回null By the way, for primitive types like int this would not even work and throw exceptions because of incompatible return types.顺便说一句,对于像int这样的原始类型,由于返回类型不兼容,这甚至不起作用并引发异常。 I was actually surprised that Spring AOP, weirdly enough, even intercepts non-void methods if the around-advice returns void, because AFAIR native AspectJ would not match non-void methods in this case.实际上,我很惊讶 Spring AOP,奇怪的是,如果环绕建议返回 void,它甚至会拦截非 void 方法,因为在这种情况下,AFAIR 原生 AspectJ 不会匹配非 void 方法。

So what can you do?所以,你可以做什么?

  • Either keep the @Around advice, if you really think you need it.如果您真的认为需要,请保留@Around建议。 Usually, this is only the case if you want to do more than log things, eg modify method parameters or return values, handle exceptions or other things potentially changing the control flow:通常,仅当您想做的不仅仅是记录事情时,例如修改方法参数或返回值、处理异常或其他可能改变控制流的事情时,才会出现这种情况:

     @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); } }
  • Or keep it simple and just use a pair of @Before and @After advice methods, if there is no need to transfer data from one advice to the other.或者保持简单,只使用一对@Before@After通知方法,如果不需要将数据从一个通知传输到另一个通知。 This is much simpler, because you do not need to proceed, use try-finally or return anything:这要简单得多,因为您不需要继续,使用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); }

    Here, you could also factor out the duplicate pointcut expression into its own @Pointcut and simply refer to it from both advice methods.在这里,您还可以将重复的切入点表达式分解到它自己的@Pointcut中,并从两个通知方法中简单地引用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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