Affects: \
- org.springframework.boot: 3.2.0
- org.springframework: 6.1.1
We used this workaround to execute suspend function in advice.
But, after updating Spring Boot to 3.2.0, ClassCastException
occurs.
stack trace
Details
java.lang.ClassCastException: class reactor.core.publisher.MonoOnErrorResume cannot be cast to class java.lang.CharSequence (reactor.core.publisher.MonoOnErrorResume is in unnamed module of loader 'app'; java.lang.CharSequence is in module java.base of loader 'bootstrap')
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106) ~[reactor-core-3.6.0.jar:3.6.0]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Handler io.github.t45k.r2dbc_aop_trial.R2dbcAopTrialApplication#aop(Continuation) [DispatcherHandler]
*__checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP GET "/blocking/aop" [ExceptionHandlingWebHandler]
Original Stack Trace:
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxFilter$FilterSubscriber.onNext(FluxFilter.java:113) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:176) ~[reactor-core-3.6.0.jar:3.6.0]
at kotlinx.coroutines.reactor.MonoCoroutine.onCompleted(Mono.kt:103) ~[kotlinx-coroutines-reactor-1.7.3.jar:na]
at kotlinx.coroutines.AbstractCoroutine.onCompletionInternal(AbstractCoroutine.kt:93) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:237) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:910) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:867) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:832) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.debug.internal.DebugProbesImpl$CoroutineOwner.resumeWith(DebugProbesImpl.kt:549) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) ~[kotlin-stdlib-1.9.0.jar:1.9.0-release-358]
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:68) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:245) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:163) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
at kotlinx.coroutines.reactive.AwaitKt$awaitOne$2$1.onComplete(Await.kt:272) ~[kotlinx-coroutines-reactive-1.7.3.jar:na]
at org.jooq.impl.R2DBC$AbstractSubscription.lambda$new$0(R2DBC.java:179) ~[jooq-3.18.7.jar:na]
at org.jooq.impl.Internal$1.onComplete(Internal.java:497) ~[jooq-3.18.7.jar:na]
at org.jooq.impl.R2DBC$AbstractResultSubscriber.lambda$onComplete$1(R2DBC.java:293) ~[jooq-3.18.7.jar:na]
at org.jooq.impl.Internal$1.onComplete(Internal.java:497) ~[jooq-3.18.7.jar:na]
at reactor.core.publisher.StrictSubscriber.onComplete(StrictSubscriber.java:123) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2231) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.pool.SimpleDequePool.maybeRecycleAndDrain(SimpleDequePool.java:537) ~[reactor-pool-1.0.3.jar:1.0.3]
at reactor.pool.SimpleDequePool$QueuePoolRecyclerInner.onComplete(SimpleDequePool.java:767) ~[reactor-pool-1.0.3.jar:1.0.3]
at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:46) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.pool.SimpleDequePool$QueuePoolRecyclerMono.subscribe(SimpleDequePool.java:879) ~[reactor-pool-1.0.3.jar:1.0.3]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:241) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoEmpty.subscribe(MonoEmpty.java:46) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:264) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:241) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onComplete(FluxHandleFuseable.java:239) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoSupplier$MonoSupplierSubscription.request(MonoSupplier.java:148) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.request(FluxHandleFuseable.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onSubscribe(MonoIgnoreElements.java:72) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onSubscribe(FluxHandleFuseable.java:164) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoSupplier.subscribe(MonoSupplier.java:48) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:264) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.0.jar:3.6.0]
at org.jooq.impl.R2DBC$AbstractNonBlockingSubscription.lambda$cancel0$4(R2DBC.java:663) ~[jooq-3.18.7.jar:na]
at java.base/java.util.concurrent.atomic.AtomicReference.updateAndGet(AtomicReference.java:210) ~[na:na]
at org.jooq.impl.R2DBC$AbstractNonBlockingSubscription.cancel0(R2DBC.java:647) ~[jooq-3.18.7.jar:na]
at org.jooq.impl.R2DBC$AbstractSubscription.complete(R2DBC.java:213) ~[jooq-3.18.7.jar:na]
at org.jooq.impl.R2DBC$AbstractResultSubscriber.complete(R2DBC.java:303) ~[jooq-3.18.7.jar:na]
at org.jooq.impl.R2DBC$AbstractResultSubscriber.onComplete(R2DBC.java:293) ~[jooq-3.18.7.jar:na]
at reactor.core.publisher.StrictSubscriber.onComplete(StrictSubscriber.java:123) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.checkTerminated(FluxWindowPredicate.java:540) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.drainLoop(FluxWindowPredicate.java:488) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.drain(FluxWindowPredicate.java:432) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.onComplete(FluxWindowPredicate.java:312) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onComplete(MonoFlatMapMany.java:260) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onComplete(FluxContextWrite.java:126) ~[reactor-core-3.6.0.jar:3.6.0]
at io.asyncer.r2dbc.mysql.internal.util.DiscardOnCancelSubscriber.onComplete(DiscardOnCancelSubscriber.java:84) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onComplete(FluxPeekFuseable.java:940) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxHandle$HandleConditionalSubscriber.onNext(FluxHandle.java:359) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:854) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.SinkManyEmitterProcessor.drain(SinkManyEmitterProcessor.java:476) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.SinkManyEmitterProcessor.tryEmitNext(SinkManyEmitterProcessor.java:273) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.SinkManySerialized.tryEmitNext(SinkManySerialized.java:100) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27) ~[reactor-core-3.6.0.jar:3.6.0]
at io.asyncer.r2dbc.mysql.client.ReactorNettyClient$ResponseSink.next(ReactorNettyClient.java:383) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
at io.asyncer.r2dbc.mysql.client.ReactorNettyClient.lambda$new$0(ReactorNettyClient.java:121) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:185) ~[reactor-core-3.6.0.jar:3.6.0]
at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:294) ~[reactor-netty-core-1.1.13.jar:1.1.13]
at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:403) ~[reactor-netty-core-1.1.13.jar:1.1.13]
at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:426) ~[reactor-netty-core-1.1.13.jar:1.1.13]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:114) ~[reactor-netty-core-1.1.13.jar:1.1.13]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.asyncer.r2dbc.mysql.client.MessageDuplexCodec.handleDecoded(MessageDuplexCodec.java:162) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
at io.asyncer.r2dbc.mysql.client.MessageDuplexCodec.channelRead(MessageDuplexCodec.java:73) ~[r2dbc-mysql-1.0.3.jar:1.0.3]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1475) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1338) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1387) ~[netty-handler-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290) ~[netty-codec-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.101.Final.jar:4.1.101.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.101.Final.jar:4.1.101.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
I guess this is due to updating AopUtils
, which calls CoroutinesUtils
.
I know this is really workaround way to invocate suspend function in AOP advice, so I can't help but think there are problems when updating the libraries.
Do you know another way to invocate suspend function in AOP advice?
Thank you.
Comment From: sdeleuze
Can you please provide a reproducer as an attached archive or a link to a repsoitory?
Comment From: T45K
@sdeleuze thank you for checking this issue! i prepared reproducing repo
https://github.com/T45K/spring-aop-around-repro
please take a look :pray:
Comment From: T45K
ah, i found a solution by myself
@Component
@Aspect
class Aspect {
@Around("xxx")
fun execute(joinPoint: ProceedingJoinPoint): Any? = mono {
callSuspendFunction()
}.then(joinPoint.proceed() as Mono<*>)
}
this looks working fine
Comment From: sdeleuze
Yeah, your solution looks fine. Before Spring Framework 6.1, there was no support for Coroutines in Spring AOP, so you had to invoke the suspending function manually like you did in your repro. As of Spring Framework 6.1, suspending functions are translated to their Reactive equivalent, as shown by your comment above.
Comment From: T45K
thank you for answering.
i have another question. do you (and spring team) have any plan to implement additional support for AOP Before and After, and Reactor (or coroutine) like the following style?
// return Mono directly as Before advice
@Before("...")
fun execute(joinPoint): Any? = mono {
callSuspendFunction()
}
// or, use suspend function directly as Before advice
@Before("...")
suspend fun execute(joinPoint): Any {
callSuspendFunction()
}
Comment From: sdeleuze
I will discuss that with the team and let you know.
Comment From: sdeleuze
We don't plan to add Coroutines dedicated support for the use cases mentioned in this comment since it would likely require, as far as I can tell, AspectJ support for Coroutines and/or Reactor which is unlikely to happen.
If you think that would be useful, we can review a related documentation PR for Kotlin documentation that would provide guidance for AspectJ + Coroutines for Spring use case.
Comment From: T45K
i see. thank you
Comment From: ilya40umov
@sdeleuze I was looking for a way to manipulate coroutine context in an advice, as it would be quite handy in order to let @Observed
annotation also support suspending methods: https://github.com/micrometer-metrics/micrometer/issues/4827
I managed to hack something together without this support for now: ObservedSuspendingAspect, but I am wondering if this is the way to go for each and every aspect that ends up needing to manipulate coroutine context.