Affects:

  • org.springframework.boot: 3.2.5
  • org.springframework: 6.1.6
  • kotlin: 1.9

After calling the suspend func, we are post-processing the response (ex logging,...) through @AfterReturning.

Until spring boot version 3.1.12, we were able to receive and process returning value as desired.

However, when we upgraded the version to 3.2.x (and even 3.3.x), an issue occurred where MonoOnErrorResume was returned as a returning value.

    @AfterReturning("execution(* x.x.x.suspendFun(..))", returning = "response")
    fun controllerSuccessLogging(joinPoint: JoinPoint, response: Any) {
        logger.info("$response")
    }

When looking at the log flow, it seems that starting from 3.2.x, the @AfterReturning part is executed before the target function ends.

boot 3.1.12

[2024-06-20 14:34:46,181] [INFO] [http-nio-8080-exec-1] [RequestLoggingAspect] API [/v1/resource/test] PARAMS [GetTestRequest(abc=123)]
[2024-06-20 14:34:46,184] [INFO] [http-nio-8080-exec-1] [MySQLExceptionHandleAspect] [SQL] info(statement=query.selResource.selectInfo parameter={abc=23} )
[2024-06-20 14:34:46,288] [INFO] [http-nio-8080-exec-1] [RequestLoggingAspect] GetTestResponse(result=ok) <-- works as intended

boot 3.2.5

[2024-06-20 14:31:52,967] [INFO] [http-nio-8080-exec-1] [RequestLoggingAspect] API [/v1/resource/test] PARAMS [GetTestRequest(abc=123)]
[2024-06-20 14:31:52,968] [INFO] [http-nio-8080-exec-1] [RequestLoggingAspect] MonoOnErrorResume  <-- issue 
[2024-06-20 14:31:52,991] [INFO] [http-nio-8080-exec-1] [MySQLExceptionHandleAspect] [SQL] info(statement=query.selResource.selectInfo parameter={abc=23} )

Are there any changes to usage, or are there any parts where support has ended?

Comment From: sdeleuze

That's related to #22462 which turns suspending functions to Mono following the logic already implemented in the rest of the Coroutines support in Spring Framework.

I believe your former use case was working only when there was no invocation of suspending function in the body of your function, otherwise you would see COROUTINE_SUSPENDED printed (for example when invoking delay(...).

You should be able to log the return value with aspects defined like:

@Around("execution(* DemoController+.*(..))")
fun afterReturningAdvice(joinPoint: ProceedingJoinPoint): Any? {
    return (joinPoint.proceed(joinPoint.args) as Mono<Any>).doOnNext { println(it) }
}

So if I am not mistaken, the behaviour you see is expected.