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

SpringBootTest中动态替换@ConfigurationProperties数据类URL属性中的随机端口

SpringBootTest中动态替换@ConfigurationProperties数据类URL属性中的随机端口

我之前也碰到过一模一样的问题,试了好几种方法踩了坑才搞明白。核心问题在于:@ConfigurationProperties是在Spring上下文初始化阶段就完成绑定的,而随机端口是Web服务器启动后才确定的;另外你原来的MyCustomProperties里url是val只读属性,就算拿到端口也没法直接修改。给你两个可行的方案,优先推荐第一种更优雅的:

方案一:用@DynamicPropertySource动态注册属性(推荐)

这个方法是Spring Boot 2.2+提供的特性,能在上下文初始化过程中动态生成属性,优先级比配置文件里的属性高,完美适配你的场景。关键是要在静态方法里通过WebServerApplicationContext获取已经启动的Web服务器端口,然后生成正确的URL。

修改你的测试类如下:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MySpringBootTest {

    @Autowired
    private lateinit var myService: MyService

    @Test
    fun testProductionMethod() {
        val result = myService.doSomething()
        println("Result: $result")
    }

    companion object {
        @DynamicPropertySource
        @JvmStatic // 必须加这个注解,让Spring能识别到这个静态方法
        fun registerDynamicProperties(registry: DynamicPropertyRegistry) {
            registry.add("my.custom.url") { context ->
                // 获取Web服务器上下文,拿到已启动的端口
                val webServerCtx = context.getBean(WebServerApplicationContext::class.java)
                val port = webServerCtx.webServer.port
                "http://localhost:$port"
            }
        }
    }
}

为什么之前用@DynamicPropertySource没成功?

大概率是没加@JvmStatic注解,Kotlin的companion object方法默认不是Java静态方法,Spring没法识别;或者没正确通过上下文获取端口,而是想直接用@LocalServerPort(但它是实例变量,静态方法里拿不到)。

这个方案不需要修改你的MyCustomProperties和配置文件,原来的application-test.yml里的my.custom.url可以保留,动态注册的属性会自动覆盖它。

方案二:修改配置类为可变属性,在@BeforeEach中替换

如果你的Spring Boot版本较低,或者不想用动态属性源,可以把MyCustomProperties里的url改成var,然后在测试初始化阶段替换值:

  1. 先修改配置类:
@ConfigurationProperties(prefix = "my.custom")
data class MyCustomProperties(
    var url: String = "" // 把val改成var,允许修改
 )
  1. 然后修改测试类:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MySpringBootTest {

    @LocalServerPort
    var port: Int = 0

    @Autowired
    private lateinit var myService: MyService

    @Autowired
    private lateinit var myCustomProperties: MyCustomProperties

    @BeforeEach
    fun setup() {
        // 在每个测试前替换URL
        myCustomProperties.url = "http://localhost:$port"
    }

    @Test
    fun testProductionMethod() {
        val result = myService.doSomething()
        println("Result: $result")
    }
 }

注意点

这个方案的缺点是破坏了配置类的不可变性(val改var),如果是生产环境的配置类,可能不太符合最佳实践,所以更推荐方案一。

另外,你之前试的ApplicationContextInitializer之所以不行,是因为它执行时机太早,Web服务器还没启动,根本拿不到随机端口;@TestPropertySource只能设置静态属性,没法动态获取端口,所以也没用。

备注:内容来源于stack exchange,提问作者Serob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 09:14:35