From 1911ca728d8827d9a558c0ea6ecca8354434e9ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Mon, 12 Aug 2024 17:27:41 +0200 Subject: [PATCH] Support invoking bridged suspending functions in AopUtils Closes gh-33045 --- .../springframework/aop/support/AopUtils.java | 7 ++-- .../aop/support/AopUtilsKotlinTests.kt | 37 +++++++++++++++++-- 2 files changed, 37 insertions(+), 7 deletions(-) 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 da5e4eefb25..be3af49edb6 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 @@ -349,9 +349,10 @@ public abstract class AopUtils { // Use reflection to invoke the method. try { - ReflectionUtils.makeAccessible(method); - return (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(method) ? - KotlinDelegate.invokeSuspendingFunction(method, target, args) : method.invoke(target, args)); + Method originalMethod = BridgeMethodResolver.findBridgedMethod(method); + ReflectionUtils.makeAccessible(originalMethod); + return (coroutinesReactorPresent && KotlinDetector.isSuspendingFunction(originalMethod) ? + KotlinDelegate.invokeSuspendingFunction(originalMethod, target, args) : originalMethod.invoke(target, args)); } catch (InvocationTargetException ex) { // Invoked method threw a checked exception. diff --git a/spring-aop/src/test/kotlin/org/springframework/aop/support/AopUtilsKotlinTests.kt b/spring-aop/src/test/kotlin/org/springframework/aop/support/AopUtilsKotlinTests.kt index a6806f90d11..9d28fca0f7d 100644 --- a/spring-aop/src/test/kotlin/org/springframework/aop/support/AopUtilsKotlinTests.kt +++ b/spring-aop/src/test/kotlin/org/springframework/aop/support/AopUtilsKotlinTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,19 +34,48 @@ class AopUtilsKotlinTests { @Test fun `Invoking suspending function should return Mono`() { val value = "foo" - val method = ReflectionUtils.findMethod(AopUtilsKotlinTests::class.java, "suspendingFunction", - String::class.java, Continuation::class.java)!! + val method = ReflectionUtils.findMethod(WithoutInterface::class.java, "handle", + String::class. java, Continuation::class.java)!! val continuation = Continuation(CoroutineName("test")) { } - val result = AopUtils.invokeJoinpointUsingReflection(this, method, arrayOf(value, continuation)) + val result = AopUtils.invokeJoinpointUsingReflection(WithoutInterface(), method, arrayOf(value, continuation)) assertThat(result).isInstanceOfSatisfying(Mono::class.java) { assertThat(it.block()).isEqualTo(value) } } + @Test + fun `Invoking suspending function on bridged method should return Mono`() { + val value = "foo" + val bridgedMethod = ReflectionUtils.findMethod(WithInterface::class.java, "handle", Object::class.java, Continuation::class.java)!! + val continuation = Continuation(CoroutineName("test")) { } + val result = AopUtils.invokeJoinpointUsingReflection(WithInterface(), bridgedMethod, arrayOf(value, continuation)) + assertThat(result).isInstanceOfSatisfying(Mono::class.java) { + assertThat(it.block()).isEqualTo(value) + } + } + @Suppress("unused") suspend fun suspendingFunction(value: String): String { delay(1) return value } + class WithoutInterface { + suspend fun handle(value: String): String { + delay(1) + return value + } + } + + interface ProxyInterface { + suspend fun handle(value: T): T + } + + class WithInterface : ProxyInterface { + override suspend fun handle(value: String): String { + delay(1) + return value + } + } + }