如何在SpringBootTest中指定nameGenerator解决Bean名称冲突
问题:@SpringBootTest指定部分类测试时Bean名称冲突,如何配置Bean名称生成器?
当用@SpringBootTest指定特定类执行测试时,由于存在两个不同包但同名的ConflictName Bean,会抛出BeanDefinitionOverrideException异常:
测试代码:
@SpringBootTest( classes = [BarService::class, ConflictName::class, com.foo.ConflictName::class, FooService::class] ) class DemoApplicationTests
异常信息:
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'conflictName' defined in null: Cannot register bean definition [Generic bean: class [com.foo.ConflictName]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] for bean 'conflictName' since there is already [Generic bean: class [com.bar.ConflictName]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] bound.
但如果不指定具体类,直接运行全应用测试则能成功执行:
@SpringBootTest class DemoApplicationTests
已知在@SpringBootApplication中可以通过nameGenerator = FullyQualifiedAnnotationBeanNameGenerator::class配置全类名作为Bean名称来避免冲突,想知道怎么在@SpringBootTest里实现同样的配置?
完整代码示例
package com.bar import org.springframework.stereotype.Service @Service class BarService(private val conflictName: ConflictName)
package com.bar import org.springframework.stereotype.Component @Component class ConflictName
package com.foo import org.springframework.stereotype.Component @Component class ConflictName
package com.foo import org.springframework.stereotype.Service @Service class FooService(private val conflictName: ConflictName)
package com import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator @SpringBootApplication(nameGenerator = FullyQualifiedAnnotationBeanNameGenerator::class) class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) }
解决方案
@SpringBootTest本身没有直接提供nameGenerator属性,但可以通过两种方式实现需求:
方式一:自定义测试配置类,指定Bean名称生成器
创建测试专用的配置类,通过@ComponentScan指定要扫描的类(或包),同时配置全类名作为Bean名称生成器:
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator import org.springframework.context.annotation.ComponentScan @Configuration @ComponentScan( basePackageClasses = [BarService::class, FooService::class], nameGenerator = FullyQualifiedAnnotationBeanNameGenerator::class ) class TestConfig
然后修改测试类,引入该配置:
@SpringBootTest(classes = [TestConfig::class]) class DemoApplicationTests
方式二:用@ContextConfiguration指定自定义容器初始化器
如果需要明确指定测试类,可自定义ApplicationContextInitializer来设置Bean名称生成器:
import org.springframework.context.ApplicationContextInitializer import org.springframework.context.ConfigurableApplicationContext import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator class BeanNameGeneratorInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> { override fun initialize(context: ConfigurableApplicationContext) { val beanFactory = context.beanFactory as org.springframework.beans.factory.support.DefaultListableBeanFactory beanFactory.beanNameGenerator = FullyQualifiedAnnotationBeanNameGenerator() } }
然后在测试类中配置初始化器:
@SpringBootTest( classes = [BarService::class, ConflictName::class, com.foo.ConflictName::class, FooService::class] ) @ContextConfiguration(initializers = [BeanNameGeneratorInitializer::class]) class DemoApplicationTests
内容的提问来源于stack exchange,提问作者Anton Balaniuc
相关产品推荐
相关产品推荐

