1. 错误信息

***************************
APPLICATION FAILED TO START
*************************** Description: The bean 'xxx.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

2. 原因

多个FeignClient访问同一个目标服务,导致value值相同

@FeignClient(value = "product.center", path = "/info")

而在FeignClient的自动配置类中(FeignClientsRegistrar.class), 注册Bean时,导致BeanName相同,报错

主要方法如下:

	public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
} for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface"); Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName()); String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration")); registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
} private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
// null beanDefinition.setPrimary(primary); String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
} BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

registerFeignClients 方法中扫描到指定包下所有的FeignClient, 并在registerFeignClient中构建对应的BeanDefinition对象并注入到容器中,但是这里可以看见, BeanName 是使用的每个FeignClient的类名:

String className = annotationMetadata.getClassName();
//...
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

所以不是FeignClient 本身的问题

但是在registerFeignClients方法中,还有一行代码:

String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));

这里注入的是每个FeignClient的自定义configuration(即FeignClient的configuration属性定义的配置类)

此类的名称取法如下:

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());
}

即可看见,在contextId 属性为空的情况下,不同的FeignClient 类,因为value值相同,所以beanName相同,生成方式如下:

registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());

3. 解决

正如错误提示信息一样, 可以加如下配置:

spring.main.allow-bean-definition-overriding=true

即当名称相同时,则覆盖

这样可以解决问题,并且网上大多数都是这么解决的,但是这样可能会有一些问题:

在上面的分析中, 因为注入每个FeignClient 的 configuration 配置信息类导致的,那么如果配置了可覆盖,那么所有的FeignClient 都是使用同一份配置,在平常使用时,如果没有配置configuration 属性,则没有问题,如果配置了,就不能使用这种方式;

第二,配置全局的可覆盖可能会隐藏简单的,代价小的,在启动时就会报错的问题, 而因为覆盖的原因,导致运行时出现无法追溯的问题;

推荐:

根据源码可以看出,

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());
}

名称首要取的是contextId属性,如果不为空,,则不会使用value作为名称,所以只需要在value相同的 FeignClient 配置不同的 contextId 即可:

@FeignClient(value = "product.center",contextId="search", path = "/info")

FeignClient 报错: A bean with that name has already been defined and overriding is disabled.的更多相关文章

  1. dubbo-admin-2.5.3 运行报错: Bean property 'URIType' is not writable or has an invalid 解决方法

    因为 jdk 是1.8的版本,和 dubbo-admin 存在兼容性问题.所以报错: Bean property 'URIType' is not writable or has an invalid ...

  2. 启动zuul时候报错:The bean 'proxyRequestHelper', defined in class path resource [org/springframework/cloud/netflix/zuul

    启动zuul时候报错:The bean 'proxyRequestHelper', defined in class path resource [org/springframework/cloud/ ...

  3. Apache报错信息之Invalid command 'Order', perhaps misspelled or defined by a module not included in the server config

    今天配置开启Apache虚拟主机时, 然后日志报错提示: Invalid command 'Order', perhaps misspelled or defined by a module not ...

  4. tomcat启动报错:Bean name 'XXX' is already used in this <beans> element

    如题,tomcat容器启动时加载spring的bean,结果报错如下: 六月 28, 2017 9:02:25 上午 org.apache.tomcat.util.digester.SetProper ...

  5. springboot 启动报错"No bean named 'org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry' available"

    1.问题 springboot启动报错 "D:\Program Files\Java\jdk-11\bin\java.exe" -XX:TieredStopAtLevel=1 -n ...

  6. Springboot2.x 启动报错:Bean named 'xxxService'... but was actually of type 'com.sun.proxy.$Proxy82'

    Springboot 2.0.5 搭建一个新项目启动后报错:Bean named 'xxxService'... but was actually of type 'com.sun.proxy.$Pr ...

  7. springboot启动报错start bean 'eurekaAutoServiceRegistration' NullPointerException

    解决方案参考:https://blog.csdn.net/hhj13978064496/article/details/82825365 我将eureka的依赖包放到了依赖包的最下面,启动报错, 如下 ...

  8. springboot easypoi 报错The bean 'beanNameViewResolver', defined in class path resource [cn/afterturn/e

    事故现场: The bean 'beanNameViewResolver', defined in class path resource [cn/afterturn/easypoi/configur ...

  9. eclipse添加web项目报错“Target runtime Apache Tomcat v7.0 is not defined.”

    项目检出后,发现是Tomcat7 发布的,修改文件: 工作空间--->项目名称--->.settings--->org.eclipse.wst.common.project.face ...

  10. Tomcat7.0.40注册到服务启动报错error Code 1 +connector attribute sslcertificateFile must be defined when using ssl with apr

    Tomcat7.0.40 注册到服务启动遇到以下几个问题: 1.启动报错errorCode1 查看日志如下图: 解决办法: 这个是因为我的jdk版本问题,因为电脑是64位,安装的jdk是32位的所以会 ...

随机推荐

  1. vue 路由守卫是否携带token

    //整个实例出来 配置路由守卫 const router = new Router({ //这里面是路由配置哈 }) router.beforeEach((to, from, next) => ...

  2. mysql系列14---mysql数据库还原与备份

    一.Liunx服务器下数据库定时备份 1.编写mysql在docker容器中备份的shell脚本: #!/bin/bash#  2020-11-15#docker启动的mysql备份mysql_use ...

  3. NetCore高级系列文章03---中间件

    .net web 一路发展过来,经历了 .net webfrom. .net Mvc ..net core不论哪种架构,都会对用户的请求到达服务器后经历一系列类似于管道的处理.在.net webfro ...

  4. Docker中Nginx部署go应用

    docker配合Nginx部署go应用 Nginx 名词解释 正向代理 反向代理 构建镜像 Nginx镜像 配置nginx.conf server_name Nginx中的负载均衡 轮询 upstre ...

  5. gym中的discrete类、box类和multidiscrete类简介和使用

    相关文章: Box() dict()可用于创建连续的空间:OpenAI Gym Discrete和Box spaces同时存在,代码该怎么写:gym中各种离散连续写法 解读gym中的action_sp ...

  6. tp使用workerman消息推送

    安装 首先通过 composer 安装 composer require topthink/think-worker SocketServer 在命令行启动服务端 php think worker:s ...

  7. Mygin之错误恢复Recover中间件

    本篇是mygin这个系列的最后一篇.如果想自己动手实现一个类似Gin的Web框架,建议从 mgin第一篇开始, 总代码行数有效行数只有600多行 github源码 mygin 目的 实现错误处理机制 ...

  8. VMware全版本下载工具

    有很多小伙伴想下载适合自己的虚拟机版本,但是官网全是英文看不懂 百度找的还怕带病毒 这里栀煜单独制作了个工具,内置vm9 10 11 12 14 15 16 17版本的虚拟机下载地址,都是官方版 不是 ...

  9. SQLServer复制表及数据的两种方法

    1.新表不存在(即复制数据的同时创建与旧表相同结构的新表):     select [col1,col2,col3...] into new_table from old_table where 1= ...

  10. JOISC 2020 记录

    Day1 T1 Building 4 首先有一个 \(O(n^2)\) 的 DP:记 \(f_{i,j,0/1}\) 表示已经填了前 \(i\) 位,其中有 \(j\) 位选择了 A 序列,当前第 \ ...