diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index 2b5b0e8c571..ed160be6f55 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -21,7 +21,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Map; -import java.util.Objects; import kotlin.Unit; import kotlin.jvm.JvmClassMappingKt; @@ -306,8 +305,12 @@ public class InvocableHandlerMethod extends HandlerMethod { @Nullable @SuppressWarnings("deprecation") - public static Object invokeFunction(Method method, Object target, Object[] args) { - KFunction function = Objects.requireNonNull(ReflectJvmMapping.getKotlinFunction(method)); + public static Object invokeFunction(Method method, Object target, Object[] args) throws InvocationTargetException, IllegalAccessException { + KFunction function = ReflectJvmMapping.getKotlinFunction(method); + // For property accessors + if (function == null) { + return method.invoke(target, args); + } if (method.isAccessible() && !KCallablesJvm.isAccessible(function)) { KCallablesJvm.setAccessible(function, true); } diff --git a/spring-web/src/test/kotlin/org/springframework/web/method/support/InvocableHandlerMethodKotlinTests.kt b/spring-web/src/test/kotlin/org/springframework/web/method/support/InvocableHandlerMethodKotlinTests.kt index 7e462c452bb..7ee5d15c88d 100644 --- a/spring-web/src/test/kotlin/org/springframework/web/method/support/InvocableHandlerMethodKotlinTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/web/method/support/InvocableHandlerMethodKotlinTests.kt @@ -98,6 +98,12 @@ class InvocableHandlerMethodKotlinTests { Assertions.assertThat(value).isEqualTo(3.1) } + @Test + fun propertyAccessor() { + val value = getInvocable(PropertyAccessorHandler::class.java).invokeForRequest(request, null) + Assertions.assertThat(value).isEqualTo("foo") + } + private fun getInvocable(clazz: Class<*>, vararg argTypes: Class<*>): InvocableHandlerMethod { val method = ResolvableMethod.on(clazz).argTypes(*argTypes).resolveMethod() val handlerMethod = InvocableHandlerMethod(clazz.constructors.first().newInstance(), method) @@ -138,6 +144,12 @@ class InvocableHandlerMethodKotlinTests { limit.value } + private class PropertyAccessorHandler { + + val prop: String + get() = "foo" + } + @JvmInline value class LongValueClass(val value: Long) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java index b52a3b97da4..490e897091d 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java @@ -24,7 +24,6 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Stream; import kotlin.Unit; @@ -306,7 +305,7 @@ public class InvocableHandlerMethod extends HandlerMethod { @Nullable @SuppressWarnings("deprecation") public static Object invokeFunction(Method method, Object target, Object[] args, boolean isSuspendingFunction, - ServerWebExchange exchange) { + ServerWebExchange exchange) throws InvocationTargetException, IllegalAccessException { if (isSuspendingFunction) { Object coroutineContext = exchange.getAttribute(COROUTINE_CONTEXT_ATTRIBUTE); @@ -318,7 +317,11 @@ public class InvocableHandlerMethod extends HandlerMethod { } } else { - KFunction function = Objects.requireNonNull(ReflectJvmMapping.getKotlinFunction(method)); + KFunction function = ReflectJvmMapping.getKotlinFunction(method); + // For property accessors + if (function == null) { + return method.invoke(target, args); + } if (method.isAccessible() && !KCallablesJvm.isAccessible(function)) { KCallablesJvm.setAccessible(function, true); } diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/InvocableHandlerMethodKotlinTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/InvocableHandlerMethodKotlinTests.kt index f253ea7df26..57ef009b935 100644 --- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/InvocableHandlerMethodKotlinTests.kt +++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/InvocableHandlerMethodKotlinTests.kt @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test import org.springframework.core.ReactiveAdapterRegistry import org.springframework.http.HttpStatus import org.springframework.http.server.reactive.ServerHttpResponse +import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.reactive.BindingContext @@ -194,6 +195,14 @@ class InvocableHandlerMethodKotlinTests { assertHandlerResultValue(result, "3.1") } + @Test + fun propertyAccessor() { + this.resolvers.add(stubResolver(Mono.empty())) + val method = PropertyAccessorController::prop.getter.javaMethod!! + val result = invoke(PropertyAccessorController(), method) + assertHandlerResultValue(result, "foo") + } + private fun invokeForResult(handler: Any, method: Method, vararg providedArgs: Any): HandlerResult? { return invoke(handler, method, *providedArgs).block(Duration.ofSeconds(5)) @@ -293,6 +302,13 @@ class InvocableHandlerMethodKotlinTests { } + class PropertyAccessorController { + + val prop: String + @GetMapping("/") + get() = "foo" + } + @JvmInline value class LongValueClass(val value: Long)