FeignClient注解中的contextId的使用场景,以及解决了什么样的问题

@FeignClient注解 中属性 contextId

比如我们有个user服务,但user服务中有很多个接口,我们不想将所有的调用接口都定义在一个类中,比如:

SpringCloud启动错误记录

1、

markdown 复制代码
***************************
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、

复制代码
Feign的服务名不能使用下划线,需使用短杠,即:"aa-bb",如果使用了下划线,汉字等也会报错

原因

  多个fegin接口使用@FeignClient注解调用同一个名称的微服务时,启动时会引发此异常。

解决方法

  方法一: 将feign接口合并。

  方法二: 在配置文件中增加配置 spring.main.allow-bean-definition-overriding=true

  方法三: 在@FeignClient注解上增加contextId属性,确保每个feign client的contextId唯一。如@FeignClient(name = "服务名", contextId="唯一名称")。

源码角度原因分析: 原因:由于name重复,而源码中又不允许BeanDefinition重复,所以导致在进行注册时报错。

①.先找到解析该注解的类FeignClientsRegistrar#registerFeignClients方法的主要代码块

scss 复制代码
public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
                //省略其他代码)
 
                //重点关注代码一
				String name = getClientName(attributes);
                //重点关注代码二
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));
 
 
				registerFeignClient(registry, annotationMetadata, attributes);
}

②.String name = getClientName(attributes);上述的该代码行的内容如下,从下列代码中可以得出该name的生成主要依赖name、value、contextId、serviceId值只取其中满足条件的第一个,因为一般情况下我们只会设置name或者value,所以该方法得到的值则为name或者value的其中之一,所以在出现有多个FeignClient接口调用同一个微服务的情况下时,就会出现值相同的name

dart 复制代码
	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());
	}

③.重点关注代码二如下

scss 复制代码
	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
        //重点关注代码
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}

④.上述的重点代码就是最终抛出异常代码的情况(在DefaultListableBeanFactory中),当name重复时,则在第二次通过name获取BeanDefinition时,结果不为空就会进行抛异常判断逻辑。

java 复制代码
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
//省略其他代码
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
            //该方法在不添加配置的情况下默认为false,所以在存在beanName一对多的情况下会抛出该异常
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
}
}

isAllowBeanDefinitionOverriding()获取的属性值虽然在该类中默认值为true,但是在程序启动的时候会使用SpringApplication中的同样的属性值来赋值,而SpringApplication中的该默认值在不存在配置值的情况下默认为false。

相关推荐
亦暖筑序6 分钟前
AgentScope-Java 入门:用 Middleware 审计 Agent 调用
java·ai编程·agentscope
你驴我16 分钟前
WhatsApp 消息撤回与编辑的幂等性设计实践
java·服务器·前端·后端·python
一缕清烟在人间20 分钟前
HarmonyOS开发实战:小分享-TextEditPage文字编辑器——Header+TextArea+工具栏
后端·华为·harmonyos·鸿蒙
程序员cxuan29 分钟前
Opus 5 深夜炸场,价格还挺香。。。
人工智能·后端·程序员
青山木1 小时前
Hot 100 ---腐烂的橘子
java·数据结构·后端·算法·leetcode·广度优先
卷无止境1 小时前
Python的collections模块:那些被低估的"瑞士军刀"
后端·python
卷无止境1 小时前
Python的方法解析顺序:一场关于继承顺序的精妙设计
后端·python
小龙报1 小时前
【优选算法】1. 水果成蓝 2.找到字符串中所有字母的异位词
java·c语言·数据结构·数据库·c++·redis·算法
宁&沉沦1 小时前
Chrome 扩展 Manifest 字段版本支持一览(全量)
前端·后端·编辑器
自强的小白2 小时前
重点!起始时间和结束时间与创建时间和修改时间的区别(localdate与LocaldateTIME区别)以及增删改查什么时候设置目前时间(添加和更新员工时)
java