Spring Cloud Open Feign系列【14】@FeignClient注解源码分析

文章目录

  • 前言
  • 作用
  • 属性配置
  • 流程解析
      1. 获取客户端名称
    1. 获取configuration
    1. 注册客户端

前言

在上篇文档中@EnableFeignClients注解,扫描@FeignClient注解表示的接口,并将加载到容器中,接下来分析下@FeignClient注解配置项及加载流程。

作用

标记接口为Feign 客户端,Feign 启动会扫描当前接口及注解属性,加载到容器中,执行时,通过IOC中当前注解对应的FactoryBean对象,创建动态代理对象,然后去执行请求流程

属性配置

@Target({
   
     ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FeignClient {
   
     
	// 指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
    @AliasFor("name")
    String value() default "";
    // 
    /** @deprecated */
    @Deprecated
    String serviceId() default "";
	// 每个客户端,对应不用的上下文,这里就是这个上下文的ID,
	// 当name属性一样时,可以自定义这个ID,来解决冲突问题
    String contextId() default "";
	
    @AliasFor("value")
    String name() default "";
	
    String qualifier() default "";
	// 指定@FeignClient调用的IP地址,一般都是服务发现,所以不需要配置
    String url() default "";
	// 是否解码404,404时,会返回NULL对象,不建议配置
    boolean decode404() default false;
	// Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
    Class<?>[] configuration() default {
   
     };
	// 定义容错的处理类,当调用远程接口失败或超时时,
	// 会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
    Class<?> fallback() default void.class;
	// 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
    Class<?> fallbackFactory() default void.class;
	// 定义当前FeignClient的统一前缀
    String path() default "";
	// 是否是primary 
    boolean primary() default true;
}

流程解析

还是一样分析FeignClientsRegistrar类。

1. 获取客户端名称

之前分析过,扫描之后,就会在registerFeignClients方法注册客户端,首先会获取客户端名称:
 
getClientName方法传入的参数,就是@FeignClient注解配置属性:
 

getClientName处理逻辑如下,也就是名称会从配置项中选择,优先级为contextId=》value=》name=》serviceId,没有则会报错。

	// 参数为
    private String getClientName(Map<String, Object> client) {
   
     
        if (client == null) {
   
     
            return null;
        } else {
   
     
        	// 先设置名称为 contextId配置项,这里没有为“”
            String value = (String)client.get("contextId");
            // 如果为空查看value配置项
            if (!StringUtils.hasText(value)) {
   
     
                value = (String)client.get("value");
            }
			 // 如果为空查看name配置项
            if (!StringUtils.hasText(value)) {
   
     
                value = (String)client.get("name");
            }
			 // 如果为空查看serviceId配置项
            if (!StringUtils.hasText(value)) {
   
     
                value = (String)client.get("serviceId");
            }
			// 如果有,则直接返回,没有报错
            if (StringUtils.hasText(value)) {
   
     
                return value;
            } else {
   
     
                throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
            }
        }
    }

2. 获取configuration

接着就是将configuration中的配置类加载到BeanDefinitionRegistry中了,这里上篇已经分析过了。
 

3. 注册客户端

接着就是registerFeignClient 注册客户端了,首先会将注解属性添加到definition中,这里url、path没有配置,所以都为空,然后注意contextId,没有配置时,默认使用name属性,这也就是为啥多个同名的@FeignClient,没有指定contextId会报错的原因了。
 
接着还会设置aliasfactoryBeanObjectType,primary属性:

		// 别名 :order-serviceFeignClient
		String alias = contextId + "FeignClient";
		// factoryBeanObjectType=》account.OrderFeign
		// 告诉factoryBean,要生成对象的类
        beanDefinition.setAttribute("factoryBeanObjectType", className);
        // 是否primary ,也就是@Primary 注解,多个相同Bean时,优先使用这一个
        boolean primary = (Boolean)attributes.get("primary");
        beanDefinition.setPrimary(primary);

最后就到了上篇文档分析的最后一步了。
 

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: