解决完上个问题,紧接着就出现这个问题The bean ‘XXX.FeignClientSpecification’ could not be registered.A bean with that name has already been defined and overriding is disabled.
问题描述
出现问题的代码如下:
// yz-service-pay模块
@FeignClient(
value = ServiceConstants.USER_SERVICE_NAME,
fallback = PayUserServiceFallBackFactory.class
)
public interface PayUserFeign extends UserFacade {
}
//yz-service-promotion模块
@FeignClient(
name = ServiceConstants.USER_SERVICE_NAME,
fallback = PromotionUserServiceFallBackFactory.class
)
public interface PromotionUserFeign extends UserFacade {
}
这个问题的原因是项目中有多个FeignClient声明的服务一样的,导致出错
解决方法
方法一:
按照他的提示在配置文件中添加配置
// .yaml文件
spring:
main:
allow-bean-definition-overriding: true
方法二:
在@FeignClient注解里面添加contextId属性
// yz-service-pay模块
@FeignClient(
contextId = "payUser",
value = ServiceConstants.USER_SERVICE_NAME,
fallback = PayUserServiceFallBackFactory.class
)
public interface PayUserFeign extends UserFacade {
}
//yz-service-promotion模块
@FeignClient(
contextId = "promotionUser",
name = ServiceConstants.USER_SERVICE_NAME,
fallback = PromotionUserServiceFallBackFactory.class
)
public interface PromotionUserFeign extends UserFacade {
}
分析:为什么要加一个contextId就不报错了呢
直接看源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
FeignClientsRegistrar类
// FeignClientsRegistrar.java
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
registerFeignClients方法 (FeignClientsRegistrar)
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 省略前部分代码
// 主要就是getClientName这个方法
String name = getClientName(attributes);
String className = annotationMetadata.getClassName();
registerClientConfiguration(registry, name, className, attributes.get("configuration"));
registerFeignClient(registry, annotationMetadata, attributes);
}
getClientName方法 (FeignClientsRegistrar)
// 返回className
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
//从缓存中获取contextId
String value = (String) client.get("contextId");
if (!StringUtils.hasText(value)) {
// 如果contextId为空,就获取value属性
value = (String) client.get("value");
}
if (!StringUtils.hasText(value)) {
// 如果value为空,就获取name属性
value = (String) client.get("name");
}
if (!StringUtils.hasText(value)) {
// 如果name为空,就获取serviceId属性
value = (String) client.get("serviceId");
}
// 返回value(此value是最终值,不是map里面存储的value)
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException(
"Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
}
通过源码分析,我们可以发现,当多个Feign客户端指向同一个服务时,如果不指定contextId,这些客户端在注册过程中可能会因为获取到相同的className而导致冲突,从而引发注册失败的问题。因此,在配置多个指向相同服务的Feign客户端时,正确设置contextId是至关重要的,以确保每个客户端都能被Spring框架唯一地识别和注册。