irpas技术客

Feign报错‘xx.FeignClientSpecification‘ could not be registered. overriding is disa

网络 4376

一、错误描述

? ? 最近使用 SpringBoot? 2.2.11.RELEASE 配合 Feign 开发是出现如下错误:

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'XXX.FeignClientSpecification' defined in null: Cannot register bean definition [Generic bean: class [org.springframework.cloud.openfeign.FeignClientSpecification]; bound. Description: The bean 'XXX.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled. 二、问题原因

根据错误描述是?The bean 'XXX.FeignClientSpecification' could not be registered.?A bean with that name has already been defined and overriding is disabled. 在项目中定义了两个 Feign 客户端 如下所示:

@FeignClient(value = "XXXX") public interface ApiService1 { @GetMapping("/user/{username}") UserDto query(@PathVariable("username") String username); } @FeignClient(value = "XXX") public interface MenuApiService { @GetMapping("/allMenus") List<MenuDto> queryAll(); }

因为 两个 Feign 客户端中配置的 value 值是一样的 导致在启动时报如上错误。

三、解决方法

在 注解 @FeignClient 中添加??contextId ?属性,并且赋予不同的值。即可解决。

四、分析

我们看在 启动类上添加的??@EnableFeignClients 注解 其中引入了??FeignClientsRegistrar 类如下所示:

?

那么我们接下来在分析一下?FeignClientsRegistrar 类的源码

?1、继承层次

我们看到此类实现了?ImportBeanDefinitionRegistrar 接口,此接口的作用就是动态注册 Bean。接口信息如下所示:

public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } } 2、FeignClientsRegistrar 类 中的实现

?在?FeignClientsRegistrar 类实现了?ImportBeanDefinitionRegistrar 接口的??registerBeanDefinitions 方法、如下所示:

接下来重点看下?registerFeignClients 方法的实现,此方法 逻辑很简单,主要就是?@EnableFeignClients 注解中设置的?clients 属性和? basePackages 属性扫描项目中所有配置了 @FeignClient? 注解的类或接口,方法解析具体可 FeignClient 客户端。看下这个方法中的主要代码(其他的可自行查看源码)如下所示:

主要代码就是:

1、调用? getClientName(attributes) 方法获取主要名称,源码如下所示:

private String getClientName(Map<String, Object> client) { if (client == null) { return null; } String value = (String) client.get("contextId"); if (!StringUtils.hasText(value)) { value = (String) client.get("value"); } if (!StringUtils.hasText(value)) { value = (String) client.get("name"); } if (!StringUtils.hasText(value)) { value = (String) client.get("serviceId"); } if (StringUtils.hasText(value)) { return value; } throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName()); }

这个方法的代码逻辑很简单,现获取 contenxtId,如果不存在在获取 value, value 不存在 获取name 依次获取。

?2、调用? registerClientConfiguration 方法注册 Bean, 源码如下所示:

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); // 关键点:根据 getClientName 方法获取的值 注册 Bean 。 registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); }

在getClientName()源码中,可以看到 name 就是 @FeignClient 注解中的 contenxtId,name,value, serviceId (已标注过期)。beanName:name + "." + FeignClientSpecification.class.getSimpleName()故而我们在使用相同名称的FeigClient注解时,注入到Ioc的是相同Bean名。所以错误是由FeignClientSpecification类引起的。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #could #not #be #registered #Overriding #is #DISA