FeignClient 报错: A bean with that name has already been defined and overriding is disabled.
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.的更多相关文章
- 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 ...
- 启动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/ ...
- 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 ...
- 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 ...
- 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 ...
- 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 ...
- springboot启动报错start bean 'eurekaAutoServiceRegistration' NullPointerException
解决方案参考:https://blog.csdn.net/hhj13978064496/article/details/82825365 我将eureka的依赖包放到了依赖包的最下面,启动报错, 如下 ...
- 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 ...
- eclipse添加web项目报错“Target runtime Apache Tomcat v7.0 is not defined.”
项目检出后,发现是Tomcat7 发布的,修改文件: 工作空间--->项目名称--->.settings--->org.eclipse.wst.common.project.face ...
- 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位的所以会 ...
随机推荐
- vue 路由守卫是否携带token
//整个实例出来 配置路由守卫 const router = new Router({ //这里面是路由配置哈 }) router.beforeEach((to, from, next) => ...
- mysql系列14---mysql数据库还原与备份
一.Liunx服务器下数据库定时备份 1.编写mysql在docker容器中备份的shell脚本: #!/bin/bash# 2020-11-15#docker启动的mysql备份mysql_use ...
- NetCore高级系列文章03---中间件
.net web 一路发展过来,经历了 .net webfrom. .net Mvc ..net core不论哪种架构,都会对用户的请求到达服务器后经历一系列类似于管道的处理.在.net webfro ...
- Docker中Nginx部署go应用
docker配合Nginx部署go应用 Nginx 名词解释 正向代理 反向代理 构建镜像 Nginx镜像 配置nginx.conf server_name Nginx中的负载均衡 轮询 upstre ...
- gym中的discrete类、box类和multidiscrete类简介和使用
相关文章: Box() dict()可用于创建连续的空间:OpenAI Gym Discrete和Box spaces同时存在,代码该怎么写:gym中各种离散连续写法 解读gym中的action_sp ...
- tp使用workerman消息推送
安装 首先通过 composer 安装 composer require topthink/think-worker SocketServer 在命令行启动服务端 php think worker:s ...
- Mygin之错误恢复Recover中间件
本篇是mygin这个系列的最后一篇.如果想自己动手实现一个类似Gin的Web框架,建议从 mgin第一篇开始, 总代码行数有效行数只有600多行 github源码 mygin 目的 实现错误处理机制 ...
- VMware全版本下载工具
有很多小伙伴想下载适合自己的虚拟机版本,但是官网全是英文看不懂 百度找的还怕带病毒 这里栀煜单独制作了个工具,内置vm9 10 11 12 14 15 16 17版本的虚拟机下载地址,都是官方版 不是 ...
- SQLServer复制表及数据的两种方法
1.新表不存在(即复制数据的同时创建与旧表相同结构的新表): select [col1,col2,col3...] into new_table from old_table where 1= ...
- JOISC 2020 记录
Day1 T1 Building 4 首先有一个 \(O(n^2)\) 的 DP:记 \(f_{i,j,0/1}\) 表示已经填了前 \(i\) 位,其中有 \(j\) 位选择了 A 序列,当前第 \ ...