单元测试无问题但Kotlin拦截器报lateinit属性us未初始化错误如何解决
异常原因
该异常的核心触发原因是:你注册Spring MVC拦截器时,使用的是手动new MyInterceptor()创建的实例,而非Spring IoC容器托管的Bean实例:
- 手动创建的实例不受Spring容器管理,
@Autowired注解不会被解析处理,因此us属性没有完成注入,触发lateinit未初始化异常 - 单元测试阶段你直接从Spring容器中获取MyInterceptor的Bean实例,依赖注入逻辑正常执行,因此单元测试无报错,仅HTTP请求触发拦截器时会抛出异常
可行解决方案
方案1:注册时使用容器托管的拦截器实例(最推荐,改造成本最低)
在你的WebMvcConfigurer实现类中,先注入已经完成属性注入的MyInterceptor Bean,再将其加入拦截器链即可:
@Configuration class WebConfig : WebMvcConfigurer { // 注入Spring容器中已经完成依赖注入的拦截器实例 @Autowired lateinit var myInterceptor: MyInterceptor override fun addInterceptors(registry: InterceptorRegistry) { registry.addInterceptor(myInterceptor) .addPathPatterns("/**") // 替换为你实际的拦截路径规则 } }
方案2:改为构造函数注入
Kotlin结合Spring开发时推荐优先使用构造函数注入,避免lateinit注入失败的问题:
第一步:修改拦截器代码
@Component class MyInterceptor(private val us: UserRepository) : HandlerInterceptor { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { // 原有业务逻辑保持不变 val token = request.getHeader("token") val userId = JWT.decode(token).audience[0] val pwd = us.findByUsername(userId) pwd?.password.println() val r = pwd?.password?.let { verifyToken(it, token) } return true } }
第二步:注册拦截器
和方案1一致,直接注入MyInterceptor实例后注册即可,Spring 4.3+版本中单实例Bean的构造参数会自动完成注入,无需额外加@Autowired注解。
方案3:手动从Spring上下文获取Bean(仅作为应急方案)
如果因项目架构限制无法修改拦截器注册逻辑,可以通过实现ApplicationContextAware接口手动获取Bean:
@Component class MyInterceptor : HandlerInterceptor, ApplicationContextAware { private lateinit var us: UserRepository override fun setApplicationContext(applicationContext: ApplicationContext) { us = applicationContext.getBean(UserRepository::class.java) } // 原有preHandle业务逻辑保持不变 }
该方案代码耦合度较高,非特殊情况不推荐使用。
内容的提问来源于stack exchange,提问作者edcjian
相关产品推荐
相关产品推荐

