在 Spring MVC 中配置默认的 Kotlin 协程上下文

问题描述 投票:0回答:2

我需要为 Spring MVC 中的所有请求配置默认协程上下文。例如

MDCContext
(与 this 类似的问题,但针对 MVC 而不是 WebFlux)。

我已经尝试过了

  1. Hook into Spring - 协程代码在here,但无法更改默认行为(需要更改
    InvocableHandlerMethod.doInvoke
    实现)
  2. 使用 AOP - AOP 和协程不能很好地配合

有什么想法吗?

spring spring-mvc kotlin-coroutines
2个回答
2
投票

这似乎有效:

@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
                        }
                    }
                }
            }
        }
    }
}

0
投票

添加到 @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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.