属性:
value:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
contextId:用来区分FeignClient实例名称
比如我们有个HBS_SERVICE服务,但HBS_SERVICE服务中有很多个对外接口,我们不想将所有的调用接口都定义在一个类中,比如:
CustomerClient1
@FeignClient(value = ServiceNameConstants.HBS_SERVICE, fallbackFactory = CustomerClientFallbackFactory.class) public interface CustomerClient1 { ...... }
CustomerClient2
@FeignClient(value = ServiceNameConstants.HBS_SERVICE, fallbackFactory = CustomerClientFallbackFactory.class) public interface CustomerClient2 { ...... }
在定义多个的时候,因为定了相同的value,启动服务会报错
Description: The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
为了解决这个错误,有两种办法:
1.避免name重复,给contextId赋值。
2.在name重复的情况下允许BeanDefinition的覆盖,则配置spring.main.allow-bean-definition-overriding=true
如果我们使用的是第二种办法,而且要自定义一个Client的configuration时,设置对应Client的configuration属性,可能配置会不生效
@FeignClient(value = ServiceNameConstants.HBS_SERVICE, configuration = FeignSupportConfig.class, fallbackFactory = CustomerClientFallbackFactory.class) public interface CustomerClient2 { ...... }
因为允许BeanDefinition的覆盖时,对应的FeignContext的配置也会覆盖,这种就需要单独设置contextId,配置才会生效
@FeignClient(contextId = "CustomerClient2", value = ServiceNameConstants.HBS_SERVICE, configuration = FeignSupportConfig1.class, fallbackFactory = CustomerClientFallbackFactory.class) public interface CustomerClient2 { ...... }
FeignClientsRegistrar#registerClientConfiguration中去名字时,会根据规则使用FeignClient的名称来生成配置bean,如果名称相同,就会覆盖
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()); }
后面在调用的时候,就会通过代理FeignClientFactoryBean#getTarget来取FeignContext
<T> T getTarget() { FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class) : applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); ...... }
因为FeignClientSpecification被覆盖了,使用配置就没有起作用