diff --git a/spring-core/src/main/java/org/springframework/core/KotlinDetector.java b/spring-core/src/main/java/org/springframework/core/KotlinDetector.java index fde4e10a6c91..920c8073c55f 100644 --- a/spring-core/src/main/java/org/springframework/core/KotlinDetector.java +++ b/spring-core/src/main/java/org/springframework/core/KotlinDetector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -27,6 +27,7 @@ * * @author Juergen Hoeller * @author Sebastien Deleuze + * @author Yanming Zhou * @since 5.0 */ @SuppressWarnings("unchecked") @@ -93,4 +94,19 @@ public static boolean isSuspendingFunction(Method method) { return false; } + /** + * Return {@code true} if the class is {@code kotlin.Unit}. + * @since 6.1.1 + */ + public static boolean isKotlinUnit(Class clazz) { + return "kotlin.Unit".equals(clazz.getName()); + } + + /** + * Return {@code true} if the object is a {@code kotlin.Unit}. + * @since 6.1.1 + */ + public static boolean isKotlinUnit(@Nullable Object object) { + return (object != null && isKotlinUnit(object.getClass())); + } } 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 87c8b3d3b4ad..0696b75f4517 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 @@ -51,6 +51,7 @@ * @author Rossen Stoyanchev * @author Juergen Hoeller * @author Sebastien Deleuze + * @author Yanming Zhou * @since 3.1 */ public class InvocableHandlerMethod extends HandlerMethod { @@ -315,7 +316,11 @@ public static Object invokeFunction(Method method, Object target, Object[] args) } } } - return function.callBy(argMap); + Object returnValue = function.callBy(argMap); + if (KotlinDetector.isKotlinUnit(returnValue)) { + returnValue = null; + } + return returnValue; } } 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 ad56c846e5c0..2b1a9eb7a49b 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 @@ -28,6 +28,7 @@ import org.springframework.web.testfixture.servlet.MockHttpServletResponse * Kotlin unit tests for {@link InvocableHandlerMethod}. * * @author Sebastien Deleuze + * @author Yanming Zhou */ class InvocableHandlerMethodKotlinTests { @@ -71,6 +72,11 @@ class InvocableHandlerMethodKotlinTests { Assertions.assertThat(value).isEqualTo("true") } + @Test + fun shouldTreatUnitAsVoid() { + Assertions.assertThat(getInvocable().invokeForRequest(request, null)).isNull() + } + private fun getInvocable(vararg argTypes: Class<*>): InvocableHandlerMethod { val method = ResolvableMethod.on(Handler::class.java).argTypes(*argTypes).resolveMethod() val handlerMethod = InvocableHandlerMethod(Handler(), method) @@ -95,6 +101,8 @@ class InvocableHandlerMethodKotlinTests { fun nullableBooleanDefaultValue(status: Boolean? = true) = status.toString() + + fun returnUnit() {} } }