Spring测试中如何在TestContainers启动后创建带自定义参数的@Bean
问题诊断
你当前的实现存在两个核心问题,导致配置无法生效:
- 执行时机错误
你在NoteTagDBInitializer的init代码块中调用Bean注册逻辑,此时Spring上下文已经完成初始化、Bean的创建与注入流程,你后注册的R2dbcEntityTemplate不会替换已经注入到其他组件中的实例,自然无法生效。 - 手动注册Bean逻辑冗余
你手动创建R2dbcEntityTemplate、ConnectionFactory的实现完全跳过了Spring Boot R2dbc的自动配置逻辑,会丢失连接池、事务管理器等内置配置,后续容易出现兼容性问题。
最优解决方案
Spring Boot 2.2.6及以上版本提供的@DynamicPropertySource注解专门用于解决Testcontainers动态端口的配置注入问题,你不需要手动注册任何Bean,直接复用生产环境的自动配置逻辑即可:
实现步骤
- 删除你编写的
TemplateFactory类,不需要手动创建R2dbc相关Bean - 修改
NoteTagDBInitializer,通过@DynamicPropertySource动态注入数据库连接配置,代码如下:
package com.wayfair.samworkgroupsservice.adapter.note import com.wayfair.samworkgroupsservice.adapter.DBInitializerInterface import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.r2dbc.core.R2dbcEntityTemplate import org.springframework.test.context.DynamicPropertyRegistry import org.springframework.test.context.DynamicPropertySource import org.testcontainers.containers.DockerComposeContainer import org.testcontainers.containers.wait.strategy.Wait import org.testcontainers.junit.jupiter.Container import org.testcontainers.junit.jupiter.Testcontainers import java.io.File @Testcontainers class NoteTagDBInitializer : DBInitializerInterface { @Autowired override lateinit var client: R2dbcEntityTemplate override val sqlScripts = listOf( "db/note/schema.sql", "db/note/reset.sql", "db/note/data.sql" ) companion object { @Container val cont: KDockerComposerContainer = KDockerComposerContainer("docker-compose.test.yml") .withExposedService( "test-db-local_1", 1433, Wait.forListeningPort() ) .withLocalCompose(true) @JvmStatic @DynamicPropertySource fun injectDynamicDbProperties(registry: DynamicPropertyRegistry) { val dbHost = cont.getServiceHost("test-db-local_1", 1433) val dbPort = cont.getServicePort("test-db-local_1", 1433) // 替换为你application.yaml中对应的r2dbc配置键即可 registry.add("spring.r2dbc.url") { "r2dbc:mssql://$dbHost:$dbPort/你的数据库名称" } registry.add("spring.r2dbc.username") { "sa" } registry.add("spring.r2dbc.password") { "Password123@#?" } } class KDockerComposerContainer(yamlFile: String) : DockerComposeContainer<KDockerComposerContainer>(File(yamlFile)) } }
注意事项
- 确保你的测试类加载了Spring上下文(比如添加了
@SpringBootTest、@DataR2dbcTest注解),@DynamicPropertySource才会被识别 - 该方案完全复用Spring Boot R2dbc的自动配置逻辑,和生产环境的Bean创建逻辑完全一致,不会出现兼容性问题
- 如果你使用的Spring Boot版本低于2.2.6,可以自定义
ApplicationContextInitializer实现动态属性注入,逻辑和上述方案一致
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

