You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin测试中如何创建ApplicationContext并解决lateinit未初始化报错

问题根因
  • 成员变量初始化时机错误:beanFactory 是类级别的成员变量,会在 TemplateFactory 实例构造阶段完成初始化,而 @Autowired 注入是在类实例创建完成后才执行的,此时 applicationContext 还未完成赋值,访问未初始化的 lateinit 变量直接触发异常。
  • 注解使用位置错误:@ContextConfiguration 是Spring Test模块专属注解,仅对测试类生效,用于指定测试上下文的配置来源,标注在普通 @Configuration 类上不会被Spring识别,对应的xml配置不会被加载,配置类的依赖注入也不会执行。
  • 测试类缺少上下文启动注解:如果你的测试类没有添加 @SpringBootTest、@ExtendWith(SpringExtension::class)(JUnit5)或 @RunWith(SpringRunner::class)(JUnit4)注解,Spring测试上下文根本不会启动,自然无法完成 ApplicationContext 的注入。
修复方案
  1. 调整beanFactory的获取时机,改为懒加载,避免类初始化阶段就访问未注入的applicationContext
  2. 将@ContextConfiguration移到测试类上,同时给测试类加上Spring测试启动注解
  3. 可选:实现ApplicationContextAware接口主动接收上下文注入,比@Autowired更稳定

修复后的TemplateFactory代码:

package com.wayfair.samworkgroupsservice.adapter

import io.r2dbc.mssql.MssqlConnectionConfiguration
import io.r2dbc.mssql.MssqlConnectionFactory
import org.springframework.beans.BeansException
import org.springframework.beans.factory.config.ConstructorArgumentValues
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.GenericBeanDefinition
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.annotation.Configuration
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate
import org.springframework.data.r2dbc.dialect.SqlServerDialect
import org.springframework.r2dbc.core.DatabaseClient

@Configuration
class TemplateFactory : ApplicationContextAware {
    private lateinit var applicationContext: ApplicationContext
    // 改为懒加载,第一次使用时才会初始化,此时applicationContext已经注入完成
    private val beanFactory: BeanDefinitionRegistry by lazy {
        applicationContext.autowireCapableBeanFactory as BeanDefinitionRegistry
    }

    override fun setApplicationContext(applicationContext: ApplicationContext) {
        this.applicationContext = applicationContext
    }

    fun registerTemplateBean(host: String, port: Int) {
        val beanDefinition = GenericBeanDefinition()
        beanDefinition.beanClass = R2dbcEntityTemplate::class.java
        val args = ConstructorArgumentValues()
        args.addIndexedArgumentValue(
            0,
            DatabaseClient.builder()
                .connectionFactory(connectionFactory(host, port))
                .bindMarkers(SqlServerDialect.INSTANCE.bindMarkersFactory)
                .build()
        )
        args.addIndexedArgumentValue(1, DefaultReactiveDataAccessStrategy(SqlServerDialect.INSTANCE))

        beanDefinition.constructorArgumentValues = args
        beanFactory.registerBeanDefinition("R2dbcEntityTemplate", beanDefinition)
    }

    private fun connectionFactory(host: String, port: Int) =
        MssqlConnectionFactory(
            MssqlConnectionConfiguration.builder()
                .host(host)
                .port(port)
                .username("sa")
                .password("Password123@#?")
                .build()
        )
}

对应的测试类写法示例(JUnit5):

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit.jupiter.SpringExtension

@ExtendWith(SpringExtension::class)
@ContextConfiguration("classpath:/testContext.xml", classes = [TemplateFactory::class])
class TemplateFactoryTest {
    @Autowired
    private lateinit var templateFactory: TemplateFactory

    @Test
    fun testRegisterBean() {
        templateFactory.registerTemplateBean("localhost", 1435)
        // 后续测试逻辑
    }
}

内容的提问来源于stack exchange,提问作者Lucas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 11:06:02