Support suspending handler methods in Spring MVC

This commit adds support for Kotlin Coroutines suspending functions to
Spring MVC, by converting those to a Mono that can then be handled by
the asynchronous request processing feature.

It also optimizes Coroutines detection with the introduction of an
optimized KotlinDetector.isSuspendingFunction() method that does not
require kotlin-reflect.

Closes gh-23611
This commit is contained in:
Sébastien Deleuze
2020-10-09 12:22:15 +02:00
parent fad7243733
commit 94a42a3086
13 changed files with 110 additions and 29 deletions

View File

@@ -16,9 +16,11 @@
package org.springframework.web.servlet.mvc.method.annotation
import kotlinx.coroutines.delay
import org.assertj.core.api.Assertions.assertThat
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.context.request.async.WebAsyncUtils
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest
import org.springframework.web.testfixture.servlet.MockHttpServletRequest
import org.springframework.web.testfixture.servlet.MockHttpServletResponse
@@ -71,6 +73,17 @@ class ServletAnnotationControllerHandlerMethodKotlinTests : AbstractServletHandl
assertThat(response.contentAsString).isEqualTo("value1-12")
}
@PathPatternsParameterizedTest
fun suspendingMethod(usePathPatterns: Boolean) {
initDispatcherServlet(CoroutinesController::class.java, usePathPatterns)
val request = MockHttpServletRequest("GET", "/suspending")
request.isAsyncSupported = true
val response = MockHttpServletResponse()
servlet.service(request, response)
assertThat(WebAsyncUtils.getAsyncManager(request).concurrentResult).isEqualTo("foo")
}
data class DataClass(val param1: String, val param2: Int)
@@ -86,4 +99,15 @@ class ServletAnnotationControllerHandlerMethodKotlinTests : AbstractServletHandl
fun handle(data: DataClassWithOptionalParameter) = "${data.param1}-${data.param2}"
}
@RestController
class CoroutinesController {
@Suppress("RedundantSuspendModifier")
@RequestMapping("/suspending")
suspend fun handle(): String {
return "foo"
}
}
}