该注解中一共有5个属性: 我们依次看,这些属性流程分析的时候都会用到:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { /** * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation * {@link #basePackages()}属性的别名。允许更简洁的注释 * * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of * {@code @ComponentScan(basePackages="org.my.pkg")}. * @return the array of 'basePackages'. */ String[] value() default {}; /** * Base packages to scan for annotated components. * 扫描带注解组件的基本包路径 * <p> * {@link #value()} is an alias for (and mutually exclusive with) this attribute. * value()是此属性的别名(并与其互斥)。 * <p> * Use {@link #basePackageClasses()} for a type-safe alternative to String-based * package names. * 使用{@link #basePackageClasses()}作为基于字符串的包名的类型安全替代方案。 * * @return the array of 'basePackages'. */ 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. * 类型安全的{@link #basePackages()}的替代方案,用于指定要扫描的包以寻找带注解的组件。 * 每个指定类的包将被扫描。 * <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'. * 考虑在每个包中创建一个特殊的无操作标记类或接口, * 该标记类或接口除了被该属性引用之外没有其他用途。 * * 可以用类或接口的方式代替字符串方式指定包名。 */ 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}. * 一个自定义的@Configuration用于所有 feign 客户端。可以包含组成客户端的各个组件的重 * 写@Bean定义,例如{@link feign.codec。解码器},{@link feign.codec。编码器}, * {@link feign.Contract}。 * * feign clients的全局配置,配置的是构成feign client的组件的具体实现类,这些类的实例最终 * 会放到每个feign client专门的一个子容器中 * * @see FeignClientsConfiguration for the defaults * @return list of default configurations */ Class<?>[] defaultConfiguration() default {}; /** * List of classes annotated with @FeignClient. If not empty, disables classpath * scanning. * 用@FeignClient注解的类列表。如果不为空,则禁用类路径扫描。 * * 通过该属性直接指定要加载哪些@FeignClient接口 * 用这个属性,就不会进行包路径扫描了 * @return list of FeignClient classes */ Class<?>[] clients() default {}; }defaultConfiguration属性可以为所有feign客户端配置默认的全局配置,可以配置如下组件:
Decoder
Encoder
Contract 就是这个接口,允许OpenFeign 支持处理 SpringMVC 相关的注解: 有兴趣自己看吧。
快速入门第三章 OpenFeign 曾用的Demo: 看到该注解中属性还是比较多的:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FeignClient { /** * 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}. * 带有可选协议前缀的服务名称。{@link #name() name}的同义词。无论是否提供url, * 都必须为所有客户端指定名称。可以指定为属性键,例如:${propertyKey}。 * * 例如http://xxxxx:port/aaa/bb/cc xxxxx就是服务名称 * * 属性键$ {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 * 带有可选协议前缀的服务id。{@link #value() value}的同义词。 * 过时了,使用{@link #name() name}代替 * * @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. * 如果存在,它将被用作bean名,代替name属性,但不会用作服务id。 * * @return bean name instead of name if present */ String contextId() default ""; /** * @return The service id with optional protocol prefix. Synonym for {@link #value() * value}. * 带有可选协议前缀的服务id。{@link #value() value}的同义词。 */ @AliasFor("value") String name() default ""; //--------- 分割线上面这些属性作用都是一样的,代表微服务名称、服务ID ----------- /** * @return the <code>@Qualifier</code> value for the feign client. * feign client的@Qualifier值。 * 根据Class Type注入,如果存在多个会报错,就需要根据名称注入@Qualifier可以指定名称 */ String qualifier() default ""; /** * @return an absolute URL or resolvable hostname (the protocol is optional). * 绝对URL或可解析主机名(协议是可选的)。 * * 配置这个相当于直连方式,例如url="localhost:8080" * 这样就不会根据微服务名称负载均衡了 */ String url() default ""; /** * @return whether 404s should be decoded instead of throwing FeignExceptions * 如果发生FeignExceptions是否需要返回404 */ boolean decode404() default false; /** * A custom <code>@Configuration</code> 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}. * 一个自定义的@Configuration用于feign client。可以包含组成客户端的组件的重写@Bean定义, * 例如{@link feign.codec。解码器},{@link feign.codec。编码器},{@link feign.Contract}。 * * 这个配置相当于是局部配置,某一个feign client专门的配置 * * @see FeignClientsConfiguration for the defaults * @return list of configurations for feign client */ 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. * 指定Feign client接口的服务降级类。服务降级类必须实现由该注解注释的接口, * 并且必须是一个有效的spring bean。 * * @return fallback class for the specified Feign client interface */ 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. * 为指定的Feign client接口定义一个服务降级工厂。服务降级工厂必须生成服务降级类的实例, * 这些实例实现了由{@link FeignClient}注释的接口。服务降级工厂必须是一个有效的spring bean。 * * @see feign.hystrix.FallbackFactory for details. * @return fallback factory for the specified Feign client interface */ 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>. * 所有方法级映射使用的路径前缀。可与或不与@RibbonClient一起使用。 */ String path() default ""; /** * @return whether to mark the feign proxy as a primary bean. Defaults to true. * 是否将feign代理标记为主bean。默认值为true。 * * 和@Primary注解类似,同一个接口多个实现类中 * 根据类型注入时候会首选被@Primary标记的实现类 * * 每个feign client接口都会为其生成一个代理类,这里是为这个代理类标记为Primary */ boolean primary() default true; }FeignClientSpecification 是一个 Feign Client 的生成规范,可以简单理解为是@EnableFeignClients注解和@FeignClient注解解析后数据存放的一个类,通过bean扫描,将扫描到的@EnableFeignClients注解的defaultConfiguration属性、@FeignClient注解的configuration属性维护在这个类中,将来初始化FeignClient子容器时会将这些类实例化到子容器,这些配置类实例都是Feign Cient构建所依赖的组件:
class FeignClientSpecification implements NamedContextFactory.Specification { //feignClient的名称,就是服务名、服务id private String name; //当前feignClient的配置类 private Class<?>[] configuration; FeignClientSpecification() { } FeignClientSpecification(String name, Class<?>[] configuration) { this.name = name; this.configuration = configuration; } ... }FeignContext 是 OpenFeign 的上下文对象,也是 Feign Client 的工厂类。 解释:就是一个Feign Client的工厂,生成Feign Client实例的。它会为每一个要创建的feign客户端(即feign client 接口)都创建一个子容器,并在子容器中注册FeignClient规范类中的配置类,这些类实例都是Feign Client创建所依赖的一些组件。而子容器的父容器就是整个应用的Spring容器。
重点要看一下父类:
configurations 维护的就是不同Feign Client名称对应的FeignClient规范。 其中包含了一个特殊的,全局的FeignClient规范:
全局的FeignClient规范:key 为 default + 当前启动类的全限定性类名,value 就是FeignClientSpecification,其configuration属性是@EnableFeignClients 中的 defaultConfiguration 属性值。这个 Entry 只有一个。就是全局的FeignClient规范。局部的FeignClient规范: key 为服务id,服务名称,value 就是FeignClientSpecification,其configuration属性是@FeignClient 的 configuration 属性值。当前应用中有多少个这个 Feign Client,那么这里就会包含多少个这种 Entry。就是每个FeignClient专门的FeignClient规范BeanDefinition 是一个 Bean 定义器,这个是Spring框架中的,OpenFeign 和 Spring 整合了嘛,初始化阶段扫描到的所有组件等都是作为BeanDefinition在Spring容器中的。 Spring解析xml时就会把一个<bean/>标签解析成一个BeanDefinition实例,最终通过BeanDefintion在Spring容器中生成一个Bean。
BeanDefinitionRegistry 是一个 BeanDefinition 注册表。