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)

Comment From: ryanjbaxter

Gradle fails to build you sample on my machine

Comment From: alexanderabramov

Fails with what error? Works for me (gradle 4.8)

gradle apitest

Task :apiTest

repro.dependencies.ClientTests > classMethod FAILED java.lang.IllegalStateException Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException

1 test completed, 1 failed

Task :apiTest FAILED

FAILURE: Build failed with an exception.

  • What went wrong: Execution failed for task ':apiTest'.

    There were failing tests. See the report at: file:///.../build/reports/tests/apiTest/index.html

Comment From: ryanjbaxter

I just noticed something else, your boot version is 2.1.0.RELEASE and you are using Finchley.SR2. This is not valid. Finchley is only compatible with Boot 2.0.x. Can you retry using the correct release?

Comment From: alexanderabramov

Ah, good catch.

Same result with Spring Boot 2.1.0.RELEASE and Spring Cloud Greenwich.M1.

Here is a new repro project, generated from start.spring.io, customized with Kotlin 1.3.10 and JUnit 5. spring-cloud-openfeign-doubleregistration-Greenwich.zip

gradlew test reports BeanDefinitionOverrideException.

Comment From: ryanjbaxter

It should be Greenwich.BUILD-SNAPSHOT

Comment From: ryanjbaxter

@hsoftxl sample?

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: alexanderabramov

Greenwich.BUILD-SNAPSHOT: spring-cloud-openfeign-doubleregistration-G-snapshot.zip

Is this sample to your satisfaction?

Comment From: xushichangdesmond

I am also hitting the same issue. The problem is that FeignClientsRegistrar#registerFeignClients calls registerClientConfiguration to register a spring bean dynamically whose name is determined by getClientName, which will be the same if two different feign client interfaces have the same name as specified in their FeignClient annotation.

spring boot 2.1 disallows multiple beans with same name by default. Notwithstanding spring boot 2.1, it is also not correct that when i have two different feign client interfaces that declare separate set of configurations that they should share the same config (because one of them overwrote the other's).

For myself, I have a temporary workaround where I use @FeignClient("myServiceName") and @FeignClient("MYSERVICENAME") in my two different feign client interfaces since spring bean name is case sensitive, while service lookup is not.

Comment From: hsoftxl

orgsean.zip

the reason is FeignClientsRegistrar.registerClientConfiguration .

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); }

if have multi FeignClient with same name, will hit this issue.

the name attr is from FeignClientsRegistrar.registerFeignClients { Map attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName());

                String name = getClientName(attributes);

} it is not contains Class info

@ryanjbaxter @alexanderabramov

Comment From: ryanjbaxter

This doesnt even seem valid, why have two separate feign clients with the same name?

Comment From: fiebiga

@ryanjbaxter I might be able to chime in with our own use case for it - we just ran into this issue as well. We had multiple controllers and multiple feign clients pointing to the same microservice. So value = "serviceA" as the micro service destination for both clients, but both clients represent different controllers or endpoints on the service

Comment From: ryanjbaxter

@fiebiga and that works today in Finchley.SR2?

Comment From: fiebiga

@ryanjbaxter I'd have to check Finchley, I'm unsure. Our upgrade from Spring 1 to 2 went straight to Greenwich.M3

Comment From: hsoftxl

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

Comment From: ryanjbaxter

Yes I understand that. This is why I am confused on the functionality that was present prior to boot 2.0.x. One bean should have been overriding the other, therefore only one should have worked. In other words this wasn't valid in 1.5.x either.

Comment From: hsoftxl

yes, it does not work if set spring.main.allow-bean-definition-overriding value "false"

Comment From: ryanjbaxter

IMO this is not a bug then, the Spring Framework has just made it obvious about what is happening now

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

Comment From: fiebiga

@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

Comment From: ryanjbaxter

I think this PR now enables that https://github.com/spring-cloud/spring-cloud-openfeign/pull/90

Comment From: fiebiga

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?

Comment From: spencergibb

The problem is using the same service with multiple @FeignClients. Undeprecating serviceId doesn't help. The problem is keys into the child application context. It's not the bean name either.

Comment From: fiebiga

@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!

Comment From: spencergibb

this issue doesn't have much to do with name or serviceid, but multiple @FeignClients.

Comment From: Alos

I've also run into this same issue. Is there a proposed fix for this?

Comment From: manishranga

I've also run into this issue, is there any fix for this?

Comment From: michalborek

In our project we use name for service discovery purpose. If we split client into multiple files, it stops working.

What is an advantage of using client name as bean name instead of using fully qualified class name?

Comment From: leiyazhen

this issue doesn't have much to do with name or serviceid, but multiple @FeignClients.

in start up class

@EnableNetplanSwagger2
@SpringCloudApplication
@EnableNetplanFeignClients
@EnableNetplanResourceServer
public class NetplanAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(NetplanAdminApplication.class, args);
    }
}

in EnableNetplanFeignClients annotation EnableFeignClients

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableNetplanFeignClients

and in remote feign class also annotation FeignClients

@FeignClient(contextId = "remoteUserAreaService", value = ServiceNameConstant.ADMIN_SERVICE)
public interface RemoteAreaService {
    @GetMapping("/dict/{label}")
    public Result<SysArea> dict(@PathVariable("label") String label);

}

is this the reason of multiple @FeignClients

Comment From: silkentrance

In my case, this was all due to an invalid path SpEL which could not be resolved, e.g.

@FeignClient(name = "audit-configuration", contextId = "audit-configuration", url = "${audit.web.url}", path = "${audit.web.base-path|/audit}/configuration")

Notice the | instead of : in the path value? This caused the whole problem and I was unable to declare multiple feign clients since the malformed SpEL expression in the path variable caused the feign client from being realized.

IMO this needs to be fixed and a proper unchecked RuntimeException must be thrown.

After fixing the SpEL expression, I was able to declare multiple feign clients.