我需要为 Spring MVC 中的所有请求配置默认协程上下文。例如
MDCContext
(与 this 类似的问题,但针对 MVC 而不是 WebFlux)。
我已经尝试过了
InvocableHandlerMethod.doInvoke
实现)有什么想法吗?
这似乎有效:
@Configuration
class ContextConfig: WebMvcRegistrations {
override fun getRequestMappingHandlerAdapter(): RequestMappingHandlerAdapter {
return object: RequestMappingHandlerAdapter() {
override fun createInvocableHandlerMethod(handlerMethod: HandlerMethod): ServletInvocableHandlerMethod {
return object : ServletInvocableHandlerMethod(handlerMethod) {
override fun doInvoke(vararg args: Any?): Any? {
val method = bridgedMethod
ReflectionUtils.makeAccessible(method)
if (KotlinDetector.isSuspendingFunction(method)) {
// Exception handling skipped for brevity, copy it from super.doInvoke()
return invokeSuspendingFunctionX(method, bean, *args)
}
return super.doInvoke(*args)
}
/**
* Copied from CoroutinesUtils in order to be able to set CoroutineContext
*/
@Suppress("UNCHECKED_CAST")
private fun invokeSuspendingFunctionX(method: Method, target: Any, vararg args: Any?): Publisher<*> {
val function = method.kotlinFunction!!
val mono = mono(YOUR_CONTEXT_HERE) {
function.callSuspend(target, *args.sliceArray(0..(args.size-2))).let { if (it == Unit) null else it }
}.onErrorMap(InvocationTargetException::class.java) { it.targetException }
return if (function.returnType.classifier == Flow::class) {
mono.flatMapMany { (it as Flow<Any>).asFlux() }
}
else {
mono
}
}
}
}
}
}
}
添加到 @Lukas 的答案:从 Spring 6.0 开始
CoroutineUtils
提供了额外的 invokeSuspendingFunction
重载,接受协程上下文。 InvocableHandlerMethod
现在还提供了受保护的 invokeSuspendingFunction
方法,可以重写该方法来利用此重载,因此您不必再从原始 spring 类中复制粘贴代码:
@Component
class ContextConfig(): WebMvcRegistrations {
override fun getRequestMappingHandlerAdapter(): RequestMappingHandlerAdapter =
CoroutineContextAwareMappingHandler()
}
class CoroutineContextAwareMappingHandler(): RequestMappingHandlerAdapter() {
override fun createInvocableHandlerMethod(handlerMethod: HandlerMethod) =
CoroutineContextAwareInvocableMethod(handlerMethod)
}
class CoroutineContextAwareInvocableMethod(handlerMethod: HandlerMethod): ServletInvocableHandlerMethod(handlerMethod) {
@Suppress("ReactiveStreamsUnusedPublisher")
override fun invokeSuspendingFunction(method: Method, target: Any, args: Array<out Any>): Any {
val coroutineContext = <initalize your context here>
return CoroutinesUtils.invokeSuspendingFunction(Dispatchers.Unconfined + coroutineContext, method, target, *args)
}
}