Move Kotlin value class unboxing to InvocableHandlerMethod
Before this commit, in Spring Framework 6.2, Kotlin value class unboxing was done at CoroutinesUtils level, which is a good fit for InvocableHandlerMethod use case, but not for other ones like AopUtils. This commit moves such unboxing to InvocableHandlerMethod in order to keep the HTTP response body support while fixing other regressions. Closes gh-33943
This commit is contained in:
@@ -16,14 +16,18 @@
|
||||
|
||||
package org.springframework.web.method.support
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.core.MethodParameter
|
||||
import org.springframework.util.ReflectionUtils
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory
|
||||
import org.springframework.web.context.request.NativeWebRequest
|
||||
import org.springframework.web.context.request.ServletWebRequest
|
||||
import org.springframework.web.testfixture.method.ResolvableMethod
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletResponse
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.test.StepVerifier
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.reflect.jvm.javaGetter
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
@@ -33,6 +37,7 @@ import kotlin.reflect.jvm.javaMethod
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class InvocableHandlerMethodKotlinTests {
|
||||
|
||||
private val request: NativeWebRequest = ServletWebRequest(MockHttpServletRequest(), MockHttpServletResponse())
|
||||
@@ -110,6 +115,12 @@ class InvocableHandlerMethodKotlinTests {
|
||||
Assertions.assertThat(value).isEqualTo("foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun resultOfUnitReturnValue() {
|
||||
val value = getInvocable(ValueClassHandler::resultOfUnitReturnValue.javaMethod!!).invokeForRequest(request, null)
|
||||
Assertions.assertThat(value).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun valueClassDefaultValue() {
|
||||
composite.addResolver(StubArgumentResolver(Double::class.java))
|
||||
@@ -138,6 +149,60 @@ class InvocableHandlerMethodKotlinTests {
|
||||
Assertions.assertThat(value).isEqualTo('a')
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClass() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
composite.addResolver(StubArgumentResolver(Long::class.java, 1L))
|
||||
val value = getInvocable(SuspendingValueClassHandler::longValueClass.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<Long>).expectNext(1L).verifyComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassReturnValue() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
val value = getInvocable(SuspendingValueClassHandler::valueClassReturnValue.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<String>).expectNext("foo").verifyComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingResultOfUnitReturnValue() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
val value = getInvocable(SuspendingValueClassHandler::resultOfUnitReturnValue.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<Unit>).verifyComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassDefaultValue() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
composite.addResolver(StubArgumentResolver(Double::class.java))
|
||||
val value = getInvocable(SuspendingValueClassHandler::doubleValueClass.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<Double>).expectNext(3.1).verifyComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithInit() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
composite.addResolver(StubArgumentResolver(String::class.java, ""))
|
||||
val value = getInvocable(SuspendingValueClassHandler::valueClassWithInit.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<String>).verifyError(IllegalArgumentException::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithNullable() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
composite.addResolver(StubArgumentResolver(LongValueClass::class.java, null))
|
||||
val value = getInvocable(SuspendingValueClassHandler::valueClassWithNullable.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<Long>).verifyComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendingValueClassWithPrivateConstructor() {
|
||||
composite.addResolver(ContinuationHandlerMethodArgumentResolver())
|
||||
composite.addResolver(StubArgumentResolver(Char::class.java, 'a'))
|
||||
val value = getInvocable(SuspendingValueClassHandler::valueClassWithPrivateConstructor.javaMethod!!).invokeForRequest(request, null)
|
||||
StepVerifier.create(value as Mono<Char>).expectNext('a').verifyComplete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun propertyAccessor() {
|
||||
val value = getInvocable(PropertyAccessorHandler::prop.javaGetter!!).invokeForRequest(request, null)
|
||||
@@ -206,23 +271,58 @@ class InvocableHandlerMethodKotlinTests {
|
||||
|
||||
private class ValueClassHandler {
|
||||
|
||||
fun valueClassReturnValue() =
|
||||
StringValueClass("foo")
|
||||
fun valueClassReturnValue() = StringValueClass("foo")
|
||||
|
||||
fun longValueClass(limit: LongValueClass) =
|
||||
limit.value
|
||||
fun resultOfUnitReturnValue() = Result.success(Unit)
|
||||
|
||||
fun doubleValueClass(limit: DoubleValueClass = DoubleValueClass(3.1)) =
|
||||
limit.value
|
||||
fun longValueClass(limit: LongValueClass) = limit.value
|
||||
|
||||
fun valueClassWithInit(valueClass: ValueClassWithInit) =
|
||||
valueClass
|
||||
fun doubleValueClass(limit: DoubleValueClass = DoubleValueClass(3.1)) = limit.value
|
||||
|
||||
fun valueClassWithNullable(limit: LongValueClass?) =
|
||||
limit?.value
|
||||
fun valueClassWithInit(valueClass: ValueClassWithInit) = valueClass
|
||||
|
||||
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) =
|
||||
limit.value
|
||||
fun valueClassWithNullable(limit: LongValueClass?) = limit?.value
|
||||
|
||||
fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor) = limit.value
|
||||
}
|
||||
|
||||
private class SuspendingValueClassHandler {
|
||||
|
||||
suspend fun valueClassReturnValue(): StringValueClass {
|
||||
delay(1)
|
||||
return StringValueClass("foo")
|
||||
}
|
||||
|
||||
suspend fun resultOfUnitReturnValue(): Result<Unit> {
|
||||
delay(1)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
suspend fun longValueClass(limit: LongValueClass): Long {
|
||||
delay(1)
|
||||
return limit.value
|
||||
}
|
||||
|
||||
|
||||
suspend fun doubleValueClass(limit: DoubleValueClass = DoubleValueClass(3.1)): Double {
|
||||
delay(1)
|
||||
return limit.value
|
||||
}
|
||||
|
||||
suspend fun valueClassWithInit(valueClass: ValueClassWithInit): ValueClassWithInit {
|
||||
delay(1)
|
||||
return valueClass
|
||||
}
|
||||
|
||||
suspend fun valueClassWithNullable(limit: LongValueClass?): Long? {
|
||||
delay(1)
|
||||
return limit?.value
|
||||
}
|
||||
|
||||
suspend fun valueClassWithPrivateConstructor(limit: ValueClassWithPrivateConstructor): Char {
|
||||
delay(1)
|
||||
return limit.value
|
||||
}
|
||||
}
|
||||
|
||||
private class PropertyAccessorHandler {
|
||||
@@ -282,4 +382,19 @@ class InvocableHandlerMethodKotlinTests {
|
||||
|
||||
class CustomException(message: String) : Throwable(message)
|
||||
|
||||
// Avoid adding a spring-webmvc dependency
|
||||
class ContinuationHandlerMethodArgumentResolver : HandlerMethodArgumentResolver {
|
||||
|
||||
override fun supportsParameter(parameter: MethodParameter) =
|
||||
"kotlin.coroutines.Continuation" == parameter.getParameterType().getName()
|
||||
|
||||
override fun resolveArgument(
|
||||
parameter: MethodParameter,
|
||||
mavContainer: ModelAndViewContainer?,
|
||||
webRequest: NativeWebRequest,
|
||||
binderFactory: WebDataBinderFactory?
|
||||
) = null
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user