From 23696b7db8e6fa7ccdfab297b25544f721f2905e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 9 Apr 2024 14:01:51 +0200 Subject: [PATCH] Add a classpath check for AOP Coroutines/Reactive conversion Closes gh-32599 --- .../org/springframework/aop/framework/CglibAopProxy.java | 5 ++++- .../springframework/aop/framework/JdkDynamicAopProxy.java | 5 ++++- .../java/org/springframework/aop/support/AopUtils.java | 8 +++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java index bbfcfed9567..b227b90d191 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java @@ -102,6 +102,9 @@ class CglibAopProxy implements AopProxy, Serializable { private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow"; + private static final boolean coroutinesReactorPresent = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt", + CglibAopProxy.class.getClassLoader());; + /** The configuration used to configure this proxy. */ protected final AdvisedSupport advised; @@ -421,7 +424,7 @@ class CglibAopProxy implements AopProxy, Serializable { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } - if (KotlinDetector.isSuspendingFunction(method)) { + if (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method)) { return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ? CoroutinesUtils.asFlow(returnValue) : CoroutinesUtils.awaitSingleOrNull(returnValue, arguments[arguments.length - 1]); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java index c9dd7443fa1..a1cd6539be0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java @@ -75,6 +75,9 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow"; + private static final boolean coroutinesReactorPresent = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt", + JdkDynamicAopProxy.class.getClassLoader());; + /** We use a static Log to avoid serialization issues. */ private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class); @@ -234,7 +237,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } - if (KotlinDetector.isSuspendingFunction(method)) { + if (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method)) { return COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()) ? CoroutinesUtils.asFlow(retVal) : CoroutinesUtils.awaitSingleOrNull(retVal, args[args.length - 1]); } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java b/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java index d7383e79ee5..0df9a367407 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java @@ -28,7 +28,6 @@ import java.util.Set; import kotlin.coroutines.Continuation; import kotlin.coroutines.CoroutineContext; import kotlinx.coroutines.Job; -import org.reactivestreams.Publisher; import org.springframework.aop.Advisor; import org.springframework.aop.AopInvocationException; @@ -65,6 +64,9 @@ import org.springframework.util.ReflectionUtils; */ public abstract class AopUtils { + private static final boolean coroutinesReactorPresent = ClassUtils.isPresent("kotlinx.coroutines.reactor.MonoKt", + AopUtils.class.getClassLoader());; + /** * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy. *

This method additionally checks if the given object is an instance @@ -347,7 +349,7 @@ public abstract class AopUtils { // Use reflection to invoke the method. try { ReflectionUtils.makeAccessible(method); - return KotlinDetector.isSuspendingFunction(method) ? + return coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method) ? KotlinDelegate.invokeSuspendingFunction(method, target, args) : method.invoke(target, args); } catch (InvocationTargetException ex) { @@ -370,7 +372,7 @@ public abstract class AopUtils { */ private static class KotlinDelegate { - public static Publisher invokeSuspendingFunction(Method method, @Nullable Object target, Object... args) { + public static Object invokeSuspendingFunction(Method method, @Nullable Object target, Object... args) { Continuation continuation = (Continuation) args[args.length -1]; Assert.state(continuation != null, "No Continuation available"); CoroutineContext context = continuation.getContext().minusKey(Job.Key);