Kotlin中Axon事件处理器与查询处理器无法协同工作问题
问题分析与解决建议
先来看你提供的代码示例:
@Component open class UserProjectionQuery { @Autowired private lateinit var repository: UserDocumentRepository @Autowired private lateinit var updateEmitter: QueryUpdateEmitter @QueryHandler fun handle(query: QueryUserIdForUsername): String? { //stuff } @EventHandler fun on(evt: UserAuthenticated) { //stuff } }
问题核心
你遇到的情况很典型:当一个类同时包含@QueryHandler和@EventHandler方法时,Spring仅生成了AnnotationQueryHandlerAdapter,却没有生成对应的AnnotationEventHandlerAdapter,最终导致repository和updateEmitter无法完成注入。更奇怪的是相同写法在Java中完全正常,这确实很可能和Spring与Kotlin的兼容逻辑有关。
可能的原因
- Kotlin类的
open特性与注解扫描的冲突:虽然你手动给类加了open关键字,但Spring对Kotlin类中混合CQRS注解的处理逻辑和Java有差异——Java类默认支持继承,而Kotlin需要显式声明,注解扫描器可能没正确识别两类注解方法的共存。 - 字段注入的初始化顺序问题:你用的是
@Autowired字段注入+lateinit的组合,这种方式在Spring处理多类型适配器时,容易因为初始化顺序紊乱导致依赖无法注入。 - 依赖版本兼容问题:如果你的Axon(从注解来看应该是用了Axon框架)、Spring Boot和Kotlin版本不匹配,也可能触发这类注解处理的Bug。
解决方案建议
1. 切换到构造注入(最推荐)
Kotlin的构造注入语法简洁,而且Spring会优先处理构造注入的依赖,能完美规避字段注入的初始化顺序问题,代码改造后如下:
@Component class UserProjectionQuery( private val repository: UserDocumentRepository, private val updateEmitter: QueryUpdateEmitter ) { @QueryHandler fun handle(query: QueryUserIdForUsername): String? { //stuff } @EventHandler fun on(evt: UserAuthenticated) { //stuff } }
如果你的项目启用了kotlin-spring插件,它会自动把Spring组件类标记为open,所以不需要手动加open关键字。
2. 检查并升级依赖版本
确认你的Spring Boot、Kotlin以及Axon框架的版本是否为最新稳定版,很多这类注解扫描的Bug都会在新版本中被修复。
3. 确认Kotlin插件配置
确保你的构建脚本中正确引入了kotlin-spring插件,它能帮助Spring更好地识别和处理Kotlin类:
// build.gradle.kts示例 plugins { id("org.springframework.boot") version "x.x.x" id("io.spring.dependency-management") version "x.x.x" kotlin("jvm") version "x.x.x" kotlin("plugin.spring") version "x.x.x" // 这个插件是关键 }
4. 临时方案优化(拆分类)
如果上述方法都无效,你当前采用的拆分类方案是可行的——把@QueryHandler和@EventHandler方法分别放到不同的@Component类中,这种方式能彻底规避混合注解的扫描问题。
总结
优先尝试构造注入的改造,这是最优雅且能解决大部分依赖注入问题的方案;如果还是不行,再考虑升级依赖或拆分类。这类问题本质上是Spring对Kotlin混合注解方法的扫描逻辑缺陷,调整注入方式或依赖版本通常能解决。
内容的提问来源于stack exchange,提问作者PolishCivil




