likes
comments
collection
share

OpenFeign源码1-环境搭建及核心类说明

作者站长头像
站长
· 阅读数 21

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

0. 环境

  • nacos版本:1.4.1
  • Spring Cloud : Hoxton.SR9(没用2020.0.2版本后面说明
  • Spring Boot :2.4.4
  • Spring Cloud alibaba: 2.2.5.RELEASE
  • Spring Cloud openFeign 2.2.2.RELEASE

测试代码:github.com/hsfxuebao/s…

2020.0.X版本开始的OpenFeign底层不再使用Ribbon了

1. 下载

github地址:github.com/spring-clou…

OpenFeign源码1-环境搭建及核心类说明

由于是maven工程,直接导入IDEA中就可以了

2. 核心类介绍

2.1 @EnableFeignClients

// @EnableFeignClients注解用来启动FeignClient,以支持Feign。
// 该注解可以通过配置,扫描指定位置的@FeignClient注解声明的Feign客户端接口
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

   // value和basePackage具有相同的功能,其中value是basePackage的别名
   // value和basePackage只能同时使用一个
   /**
    * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
    * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
    * {@code @ComponentScan(basePackages="org.my.pkg")}.
    * @return the array of 'basePackages'.
    */
   // 为basePackages属性的别名,允许使用更简洁的书写方式。例如:@EnableFeignClients({"com.cd", "com.ad"})
   String[] value() default {};

   /**
    * Base packages to scan for annotated components.
    * <p>
    * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
    * <p>
    * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
    * package names.
    * @return the array of 'basePackages'.
    */
   // 设置自动扫描带有@FeignClient注解的基础包路径。例如 @EnableFeignClients(basePackages = {"com.cd", "com.ad"})
   String[] basePackages() default {};

   /**
    * Type-safe alternative to {@link #basePackages()} for specifying the packages to
    * scan for annotated components. The package of each class specified will be scanned.
    * <p>
    * Consider creating a special no-op marker class or interface in each package that
    * serves no purpose other than being referenced by this attribute.
    * @return the array of 'basePackageClasses'.
    */
   // 该属性是basePackages属性的安全替代属性。该属性将扫描指定的每个类所在的包下面的所有被@FeignClient修饰的类;
   // 这需要考虑在每个包中创建一个特殊的标记类或接口,该类或接口除了被该属性引用外,没有其他用途
   Class<?>[] basePackageClasses() default {};

   /**
    * A custom <code>@Configuration</code> for all feign clients. Can contain override
    * <code>@Bean</code> definition for the pieces that make up the client, for instance
    * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
    *
    * @see FeignClientsConfiguration for the defaults
    * @return list of default configurations
    */
   // 该属性用来自定义所有Feign客户端的配置,使用@Configuration进行配置。
   // 当然也可以为某一个Feign客户端进行配置。具体配置方法见@FeignClient的configuration属性。
   Class<?>[] defaultConfiguration() default {};

   /**
    * List of classes annotated with @FeignClient. If not empty, disables classpath
    * scanning.
    * @return list of FeignClient classes
    */
   // 设置由@FeignClient注解修饰的类列表。如果clients不是空数组,则不通过类路径自动扫描功能来加载FeignClient
   // 例如 @EnableFeignClients(clients = {SchedualService.class})
   // 上面代码中引入FeignClient客户端SchedualService,且也只引入该FeignClient客户端。
   Class<?>[] clients() default {};

}

2.2 @FeignClient

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {

   // name 和 value 两个属性等价,至少要配置一个
   /**
    * The name of the service with optional protocol prefix. Synonym for {@link #name()
    * name}. A name must be specified for all clients, whether or not a url is provided.
    * Can be specified as property key, eg: ${propertyKey}.
    * @return the name of the service with optional protocol prefix
    */
   @AliasFor("name")
   String value() default "";

   /**
    * The service id with optional protocol prefix. Synonym for {@link #value() value}.
    * @deprecated use {@link #name() name} instead
    * @return the service id with optional protocol prefix
    */
   @Deprecated
   String serviceId() default "";

   /**
    * This will be used as the bean name instead of name if present, but will not be used
    * as a service id.
    * @return bean name instead of name if present
    */
   // 别名,假设一个User服务有两个FeignClient,都需要调用Product服务, 因为name属性值一样,所以需要通过配置contextId来区分,否则启动项目时会报错
   String contextId() default "";

   /**
    * @return The service id with optional protocol prefix. Synonym for {@link #value()
    * value}.
    */
   @AliasFor("value")
   String name() default "";

   /**
    * @return the <code>@Qualifier</code> value for the feign client.
    */
   // 返回默认值=contextId+“FeignClient”。
   String qualifier() default "";

   /**
    * @return an absolute URL or resolvable hostname (the protocol is optional).
    */
   // 请求地址, 没配置的话, 会把name/value的属性值当成服务名进行调用, 配置的话则使用url的值
   String url() default "";

   /**
    * @return whether 404s should be decoded instead of throwing FeignExceptions
    */
   // 当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
   boolean decode404() default false;

   /**
    * A custom configuration class for the feign client. Can contain override
    * <code>@Bean</code> definition for the pieces that make up the client, for instance
    * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
    *
    * @see FeignClientsConfiguration for the defaults
    * @return list of configurations for feign client
    */
   // Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
   Class<?>[] configuration() default {};

   /**
    * Fallback class for the specified Feign client interface. The fallback class must
    * implement the interface annotated by this annotation and be a valid spring bean.
    * @return fallback class for the specified Feign client interface
    */
   // 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口
   Class<?> fallback() default void.class;

   /**
    * Define a fallback factory for the specified Feign client interface. The fallback
    * factory must produce instances of fallback classes that implement the interface
    * annotated by {@link FeignClient}. The fallback factory must be a valid spring bean.
    *
    * @see feign.hystrix.FallbackFactory for details.
    * @return fallback factory for the specified Feign client interface
    */
   // 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
   Class<?> fallbackFactory() default void.class;

   /**
    * @return path prefix to be used by all method-level mappings. Can be used with or
    * without <code>@RibbonClient</code>.
    */
   // 所有方法级映射使用的路径前缀
   String path() default "";

   /**
    * @return whether to mark the feign proxy as a primary bean. Defaults to true.
    */
   // 是否将外部代理标记为主bean。默认为true。
   boolean primary() default true;

}

2.3 FeignClientSpecification

FeignClientSpecification 是 Feign Client 生成规范:

OpenFeign源码1-环境搭建及核心类说明

2.4 FeignContext

FeignContext 是一个为 Feign Client 创建所准备的上下文对象

// FeignContext 是一个为 Feign Client 创建所准备的上下文对象
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {

   public FeignContext() {
      super(FeignClientsConfiguration.class, "feign", "feign.client.name");
   }

}

其父类为:

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
      implements DisposableBean, ApplicationContextAware {

   private final String propertySourceName;

   private final String propertyName;

   // todo 该 map 的 key 为 FeignClient 的名称(其所要调用的微服务名称), value 是组装这个
   // FeignClient 所必须的组件所在的 Spring 子容器
   private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

    // 这个map中存放的是@EnableFeignClients与@FeignClient两个注解中的configuration属性值。
    // 这个属性值只有两类:
    // 第一类只有一个,其 key 为字符串 default + 当前启动类的全限定性类名 ,例如:
// default.com.abc.ConsumerFeign8080, value 为@EnableFeignClients 的 defaultConfiguration属性值
    // 第二类有多个,其 key 为当前@FeignClient 的名称, value 为这个注解的 configuration 属性值
   private Map<String, C> configurations = new ConcurrentHashMap<>();

   private ApplicationContext parent;

   private Class<?> defaultConfigType;

   public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
         String propertyName) {
      this.defaultConfigType = defaultConfigType;
      this.propertySourceName = propertySourceName;
      this.propertyName = propertyName;
   }

参考文章

spring-cloud-openfeign-2.2.2源码分析 springcloud-source-study学习github地址 图说系列:OpenFeign源码解析 Spring Cloud 源码分析之OpenFeign Spring Cloud openFeign学习【3.0.2版本】 微服务入门到入土