Kotlin与Spring Boot依赖注入时类实例重置问题
问题根因
你碰到的核心问题是Spring代理实例与原始实例不统一:prepare方法操作的是Spring生成的CGLIB代理对象,而handleRequest方法调用的是原始的FileStreamRequestHandler对象——代理对象里设置的objectId、accessId等变量,原始对象根本没拿到,自然触发lateinit var未初始化的错误。
常见触发场景:
- Kotlin默认类和方法都是
final的,CGLIB没法代理final方法。如果handleRequest是final,调用时会直接绕过代理走原始对象,导致实例不匹配。 - 若在
prepare里直接用this.handleRequest()调用方法,内部调用不会经过Spring代理逻辑,直接触发原始对象的方法。 FileStreamRequestHandler的Bean作用域是prototype,每次从容器拿的都是新实例,prepare和handleRequest请求拿到的不是同一个对象。
解决办法
1. 让类和方法可被代理(Kotlin专属)
把FileStreamRequestHandler类和需要代理的方法加上open关键字,允许Spring生成CGLIB代理:
open class FileStreamRequestHandler { lateinit var objectId: String lateinit var accessId: String open fun prepare(params: ...) { this.objectId = params.objectId this.accessId = params.accessId // 内部调用改用代理实例 self.handleRequest() } open fun handleRequest() { // 业务逻辑 } // 注入自身的代理实例 @Autowired private lateinit var self: FileStreamRequestHandler }
2. 避免内部调用绕过代理
如果必须在类内部调用方法,不要用this,改用Spring容器提供的代理实例:
- 方法一:注入自身代理(如上代码)
- 方法二:用
AopContext获取当前代理
open class FileStreamRequestHandler { open fun prepare(params: ...) { this.objectId = params.objectId val proxy = AopContext.currentProxy() as FileStreamRequestHandler proxy.handleRequest() } }
注意:用AopContext需要在启动类加@EnableAspectJAutoProxy(exposeProxy = true)。
3. 统一Bean作用域
确保FileStreamRequestHandler是单例(Spring默认),除非业务明确需要多实例:
@Component // 默认singleton作用域 open class FileStreamRequestHandler { // ... }
如果确实要用prototype,得保证同一个请求内复用同一个实例,比如加@RequestScope注解。
4. 强制使用CGLIB代理
如果FileStreamRequestHandler实现了接口,Spring默认用JDK动态代理,可能导致代理类型冲突。在启动类加注解强制用CGLIB:
@SpringBootApplication @EnableAspectJAutoProxy(proxyTargetClass = true) class YourApplication
验证要点
- 调试时对比
prepare和handleRequest里的实例ID,确认是同一个代理对象。 - 检查
FileStreamRequestHandler的类和prepare、handleRequest方法都加了open。 - 确认内部方法调用用的是代理实例,不是直接
this调用。
内容的提问来源于stack exchange,提问作者mihirs
相关产品推荐
相关产品推荐

