Feign源码详解

一,入口 —— Feign的核心注解

Feign是我们在分布式开发中常用的RPC框架,关于Feign远程调用的秘密,我有很多想要探究的例如:

  • Feign是如何收集FeignClient的?
  • Feign是如何配置FeignClient的,让其拥有降级重试的能力?
  • Feign整个远程调用的流程是怎么样的?
  • Feign他是如何创建代理类的?
  • Feign他是怎么去将一个Interface里面的接口方法作为请求发送的?
  • Feign是如何兼容这么多HttpClient框架的?
  • Feign他是怎么做到负载均衡的,如何和Ribbon结合的?

这么多繁杂的问题,往往扰人心智,不如我们从我们开发中最常见最常用的两个注解入手。

①,@EnableFeignClients

这个注解通常标注在启动类上,作用看起来和@SpringBootApplication一样,我们姑且猜测他的职能也和@SpringBootApplication一样是负责开启Feign功能以及扫描相关类,我们直接进入@EnableFeignClients来探究源码。

@Import({
   FeignClientsRegistrar.class})
public @interface EnableFeignClients {
   
    // 根据包路径扫描其下的FeignClient,类似于basePackages的简写
    String[] value() default {
   };

    // 根据包路径扫描其下的FeignClient
    String[] basePackages() default {
   };

    // 指定标记的接口来扫描包
    Class<?>[] basePackageClasses() default {
   };

    // Feign客户端默认的全局配置类
    Class<?>[] defaultConfiguration() default {
   };

    // 指定@FeignClient注解的类,直接使用这几个@FeignClient,此时会ban掉类路径扫描
    Class<?>[] clients() default {
   };
}

可以通过源码和注释看出来,@EnableFeignClients的作用是用来扫描@FeignClient标注的类和指定全局配置类,值得注意的是在该注解最上方还有一个

@Import({FeignClientsRegistrar.class})

在我们使用@EnableFeignClientsFeignClientsRegistrar也会被引入至SpringBean容器的上下文。从名称可以看出这是FeignClient的注册类,我们再大胆猜测一下,FeignClientsRegistrar该类的职能应该是扫描和注册FeignClients,他肯定是接下来我们源码阅读的关接类之一,再次我们现在这里将其Mark一下,稍后回来。

②,@FeignClient

对于这个接口我们通常是标记在一个远程调用RPC的Interface类上面,但是其作用是用来标记还是用来声明呢,这个作用其实光从名称很难理解,我们可以看看作者留给我们的注释来熟悉一二:

注解用于声明某接口应创建为 REST 客户端(例如,用于自动注入到其他组件中)。如果 SC LoadBalancer 可用,它将用于对后端请求进行负载均衡,且负载均衡器可以使用与 Feign 客户端相同的名称(即 value)进行配置。

看完注释我们再结合代码来进行理解:

public @interface FeignClient {
   

	@AliasFor("name")
    // 服务名称 (name的简化版)
	String value() default "";
    
	// 接口生成的动态代理的bean Id
	String contextId() default "";

	@AliasFor("value")
     // 服务名称 (name的简化版)
	String name() default "";
    
	String[] qualifiers() default {
   };

    // 服务的url,对于开启了Loadbalance的服务无需使用url,如果没有开启则需要一个绝对的地址
	String url() default "";

    // 如果为false则404时默认抛出 FeignException异常,为true则抛出404异常
	boolean decode404() default false;

    // Feign客户端本身的配置类,可以对 feign.codec.Decoder, feign.codec.Encoder, feign.Contract.等等进行自定义
	Class<?>[] configuration() default {
   };

    // 失败回调方法
	Class<?> fallback() default void.class;
	
    // 用于生成fallback类实例
	Class<?> fallbackFactory() default void.class;
	
    // 定义当前FeignClient的统一前缀
	String path() default "";

	boolean primary() default true;

}

看完源码和注释后,我们可以总结出FeignClient的功能:

用来声明FeignClient的一些基本属性,例如:bean name,url,服务名称,path等等

对FeignClient的一些核心功能组件进行配置,核心配置组件如下图所示(截图取自 bojiangzhou 大佬)

image

③ , Feign客户端配置和全局配置

看到上面两个主即我们可以发现,不管是@EnableFeignClients@FeignClient 都有配置项选择,那对于配置项我们该怎么使用呢

1,yaml配置项

feign:
 client:
  config:
   # 默认全局配置
   default:
   # 指定客户端名称
   genius-client:

2,代码配置项

public class GeniusFeignConfiguration {
   

    @Bean
    public Retryer feignRetryer() {
   
        return new Retryer.Default();
    }
}
@FeignClient(value = "genius-client", configuration = GeniusFeignConfiguration.class)
public interface GeniusFeignClient{
   
    
}

可以看到对于客户端配置和全局配置的使用都很简单方便,但是值得注意的一点是这两个的配置项使用优先级是不同,这个我们后续再谈。

本章导图

image

二,FeignClientsRegister

我们重新回到之前mark的FeignClientsRegister先来看看整个类的实现

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware{
   
    
    private ResourceLoader resourceLoader;
    
    private Environment environment;
}

可以看到FeignClientsRegister实现了三个接口

  • ResourceLoaderAware:注入资源加载器 ResourceLoader
  • EnvironmentAware:注入环境 Environment
  • ImportBeanDefinitionRegistrar:注册并注入BeanDefinition

BeanDefinition 实际上是Spring bean的元数据,它保存了Bean的很多属性,我们通常注册BeanDefinition的方式有 @Component,@Bean等。而实现ImportBeanDefinitionRegistrar也是一种注册BeanDefinition 的方式,它通过registerBeanDefinitions 方法来实现BeanDefinition 的注册。

通过这三个实现的接口,我们也找到了核心函数 registerBeanDefinitions,我们来看看他的代码:

/**
 * @param metadata @EnableFeignClients 注解的元数据
 * @param registry BeanDefinition 注册器
 */
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   
    // 注册默认配置项
    registerDefaultConfiguration(metadata, registry);
    // 注册FeignClients
    registerFeignClients(metadata, registry);
}

可以看出整个BeanDefinitions的注册分为两个步骤:注册默认配置项注册FeignClients

① 注册默认配置项

这一步是将@EnableFeignClients上的defaultConfiguration内容,给注册为BeanDefinitions,如果不存在则不注册

private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   
    // 获取注解上的属性信息
   Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
	
    // defaultConfiguration不为空则注入自定义默认配置项
   if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
   
      String name;
       // 查看注解是不是标记在内部类上
      if (metadata.hasEnclosingClass()) {
   
         name = "default." + metadata.getEnclosingClassName();
      }
      else {
   
         name = "default." + metadata.getClassName();
      }
       // 注册自定义默认配置项
      registerClientConfiguration(registry, name, defaultAttrs.get(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值