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

一、该注解的部分属性值如下:

  1. value/name:指定提供者的微服务名称
  2. url:直接指定请求的路径地址
  3. decode404:是否应该编码或者抛出FeignException异常
  4. configuration:配置feign.codec.Decoder、feign.codec.Encoder、feign.Contract
  5. fallback:指定发送异常调用或者超时时应该调用那个类来执行备用方法
  6. fallbackFactory:提供统一的异常熔断处理,避免重复代码的编写
  7. path:当服务提供者使用了server.context.path时。
  8. contextId:用来唯一标识当一个微服务中存在多个FeignClient接口调用同一个服务提供者时的场景(若是不提供该属性值,则在程序启动时会启动失败,并提示如下信息)
***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'service-provider8000.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

二、启动失败问题原因

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

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]。

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

③.重点关注代码二如下

	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时,结果不为空就会进行抛异常判断逻辑。

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。

三、结论

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

解决方式:

【1】避免name重复,给contextId赋值。

【2】在name重复的情况下允许BeanDefinition的覆盖,则配置spring.main.allow-bean-definition-overriding=true(至于覆盖了BeanDefinition会有什么问题则需要进一步查看)

  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长了脚の妖怪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值