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)

Most upvoted comments

@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

@Autowired
@Qualifier("feignClientA")
FeignClient feignClientA;

We load them in by:

@Autowired
FeignClientA feignClientA;

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