Spring/Kotlin按嵌套泛型QueryHandler接口类型无法找到对应Bean问题求解
问题根源
JVM泛型擦除机制下,你传入的KClass<TResult>仅能代表原始类型,当TResult是List<UserDto>这类嵌套泛型类型时,resultType.java实际得到的是裸List类,嵌套的UserDto泛型信息被擦除,所以ResolvableType构造出的第二个泛型参数为List<?>,无法匹配实现类中List<UserDto>的完整类型。
可行解决方案
方案1:用Kotlin具体化泛型传递完整泛型信息(最推荐)
利用Kotlin inline + reified 具体化泛型特性,配合标准库typeOf() API(Kotlin 1.3及以上支持)拿到包含嵌套泛型的完整类型信息,再构造ResolvableType:
- 先新增一个inline重载方法,对外暴露使用:
import kotlin.reflect.full.typeOf import org.springframework.core.ResolvableType inline fun <reified TQuery : Query<TResult>, reified TResult : Any> dispatch(query: TQuery): TResult { // 拿到带完整嵌套泛型的KType return dispatch(query, TQuery::class, typeOf<TResult>()) }
- 修改原有dispatch方法的参数,接收
KType作为结果类型入参:
override fun <TQuery : Query<TResult>, TResult : Any> dispatch( query: TQuery, queryType: KClass<TQuery>, resultType: KType ): TResult { val beanFactory = applicationContext.beanFactory // 分别构造两个泛型参数的完整ResolvableType val queryResolvableType = ResolvableType.forClass(queryType.java) val resultResolvableType = ResolvableType.forType(resultType.javaType) // 组装QueryHandler的完整泛型类型 val handlerType = ResolvableType.forClassWithGenerics( QueryHandler::class.java, queryResolvableType, resultResolvableType ) val beans = applicationContext.getBeanNamesForType(handlerType) val handler = beanFactory.getBean<QueryHandler<TQuery, TResult>>(beans.first()) return handler.execute(query) }
调用的时候直接用泛型指定即可,不需要手动传类型参数:
val users: List<UserDto> = dispatch(GetUsers())
方案2:手动校验泛型(兜底方案)
如果不想修改原有方法签名,可以先捞出所有QueryHandler实现类,反射校验泛型匹配:
override fun <TQuery : Query<TResult>, TResult : Any> dispatch(query: TQuery, queryType: KClass<TQuery>, resultType: KClass<TResult>): TResult { val handlers = applicationContext.getBeansOfType(QueryHandler::class.java) for ((_, handler) in handlers) { val handlerType = ResolvableType.forInstance(handler).`as`(QueryHandler::class.java) // 校验查询类型匹配 if (handlerType.getGeneric(0).resolve() != queryType.java) continue // 校验结果类型匹配,支持嵌套泛型校验 val handlerResultType = handlerType.getGeneric(1) val expectedResultType = ResolvableType.forType(resultType.java) if (handlerResultType.isAssignableFrom(expectedResultType)) { @Suppress("UNCHECKED_CAST") return handler as QueryHandler<TQuery, TResult> } } throw NoSuchElementException("No matching QueryHandler found") }
这个方案性能略低于方案1,适合并发量不高的场景。
内容的提问来源于stack exchange,提问作者TheSpixxyQ
相关产品推荐
相关产品推荐

