spring-cloud-openfeign: BeanDefinitionOverrideException in FeignClientsRegistrar in tests with customized Spring context
I have a Spring Boot application and some “API tests” for it, where I want to reuse some Feign clients from the main application, but not the whole Spring context.
The setup works using @SpringBootTest
, @ImportAutoConfiguration
and @EnableFeignClients
, as long as feign clients are in non-overlapping packages.
As soon as I create a new Feign client in an overlapping package, I get an exception when SpringBootTest context is being created (sample project attached). spring-cloud-openfeign-doubleregistration.zip
Looks like FeignClientsRegistrar sees two FeingClients: repro.integration.client1.Client1
and repro.Client2
, determines that there are 2 base packages repro.integration.client1
and repro
, and then tries to register repro.integration.client1.Client1
twice - once for base package repro.integration.client1
and second time for repro
.
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246) at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:97) … Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name ‘client1.FeignClientSpecification’ defined in null: Cannot register bean definition [Generic bean: class [org.springframework.cloud.openfeign.FeignClientSpecification]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean ‘client1.FeignClientSpecification’: There is already [Generic bean: class [org.springframework.cloud.openfeign.FeignClientSpecification]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound. at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:894) at org.springframework.cloud.openfeign.FeignClientsRegistrar.registerClientConfiguration(FeignClientsRegistrar.java:374) at org.springframework.cloud.openfeign.FeignClientsRegistrar.registerFeignClients(FeignClientsRegistrar.java:155) at org.springframework.cloud.openfeign.FeignClientsRegistrar.registerBeanDefinitions(FeignClientsRegistrar.java:83) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:364) at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:363) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:145) at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:117) at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:327) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:691) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:528) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 1
- Comments: 31 (11 by maintainers)
@spencergibb But that’s only a problem because of the requirement on ‘name’ being explicitly defined, yes? Most spring beans don’t require a name unless it’s of the same class type… which I understand they’re all FeignClients, but they’re also all feign clients generated from different classes, which seems like could be done deterministically?
We don’t inject feignclients via
We load them in by:
Which seems like requiring ‘name’ just invites the bean conflict itself, rather than making it purely optional, and leveraging serviceId for the ‘multiple clients, 1 service’ problem
I appreciate your time in responding!
I have one question on #90 … I apologize if this is the wrong place to do it. But why do it that way? It seems opposite. Why not undeprecate serviceId, make the bean name deterministic based on class name, or not required at all, and just use serviceId as the feignclient service lookup?
@ryanjbaxter Wouldn’t it be more appropriate to separate the concerns of the bean name and the service name for the feign client? I see there is a deprecated field for a service name on the feign client annotation - it strikes me that that is the more appropriate workaround for this than enabling bean overrides when that might not be the desired behavior
spring boot 2.0.x spring.main.allow-bean-definition-overriding default value is “true”
spring boot 2.1.x default value changed to “false”
@ryanjbaxter