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中render函数使用attrs绑定id、class、style、事件(5)

    1.h函数的三个参数 第一个参数是必须的. 类型:{String | Object | Function} 一个 HTML 标签名.一个组件.一个异步组件.或一个函数式组件. 是要渲染的html标签. ...

  2. 火狐谷歌浏览器去掉input type=number时控件的方法

    html 默认:<input type="number" /></br> 处理:<input type="number" clas ...

  3. 通杀无限 debugger,目前只有 1% 的人知道!

    前言 相信很多小伙伴在进行 web 逆向的时候,都遇到过无限 debugger.最简单的方法,在 debugger 位置,点击行号,右键 Never pause here,永远不在此处断下即可.但是这 ...

  4. jenkins上发布项目后将文件推送到另一台服务器build镜像

    1.配置jenkins 1)主页面点击manage jenkins 2) 选择SystemConfiguration 3) 配置服务器信息,注意密码一定要输入对,还有端口有,一般是默认的22端口,但有 ...

  5. Protocol Buffer命名空间冲突

    原文在这里. 什么是Protocol Buffer命名空间冲突? 所有链接到Go二进制文件的Protocol Buffer声明都被插入到一个全局注册表中. 每个Protocol Buffer声明(例如 ...

  6. silce的扩容,截取,使用规范总结

    切片 什么是slice slice的创建使用 slice使用的一点规范 slice和数组的区别 slice的append是如何发生的 复制Slice和Map注意事项 接收 Slice 和 Map 作为 ...

  7. C++ Boost库 操作字符串与正则

    字符串的查找与替换一直是C++的若是,运用Boost这个准标准库,将可以很好的弥补C++的不足,使针对字符串的操作更加容易. 字符串格式转换: #include <iostream> #i ...

  8. 给Java小白,整理一套能上手的简单编程算法题!!!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 数学离程序员有多近? ifelse也好.for循环也罢,代码可以说就是对数学逻辑的具 ...

  9. 开发必备,开源 or 免费的 AI 编程助手

    AI 大模型的火热,让开发圈近来如虎添翼,各种各样基于 AI 技术的开发者工具和新范式不断涌现,尤其是 Github 和 OpenAI 共同推出的 Copilot X ,更是一骑绝尘.本文推荐一些开源 ...

  10. P7167 [eJOI2020 Day1] Fountain 题解

    题目链接:Fountain 很不错的基础算法组合题:单调栈+倍增 首先考虑到一个事实,就是下面第一个比当前半径大的位置会成为移动的第一次落脚点,抽象下就是下面出现的第一次比自身大的半径,这个问题显然可 ...