天天看點

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

重要類與接口解析

1. @EnableFeignClients

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

該注解中一共有5個屬性:

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

我們依次看,這些屬性流程分析的時候都會用到:

@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
    Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口
  • Encoder
    Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口
  • Contract
    Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口
    就是這個接口,允許OpenFeign 支援處理 SpringMVC 相關的注解:
    Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口
    有興趣自己看吧。

2. @FeignClient

快速入門第三章 OpenFeign 曾用的Demo:

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

看到該注解中屬性還是比較多的:

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口
@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;

}
           

3. FeignClientSpecification 類

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;
	}
	...
}
           

4. FeignContext 類

FeignContext 是 OpenFeign 的上下文對象,也是 Feign Client 的工廠類。

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

解釋:就是一個Feign Client的工廠,生成Feign Client執行個體的。它會為每一個要建立的feign用戶端(即feign client 接口)都建立一個子容器,并在子容器中注冊FeignClient規範類中的配置類,這些類執行個體都是Feign Client建立所依賴的一些元件。而子容器的父容器就是整個應用的Spring容器。

重點要看一下父類:

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

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規範

5. BeanDefinition 接口

BeanDefinition 是一個 Bean 定義器,這個是Spring架構中的,OpenFeign 和 Spring 整合了嘛,初始化階段掃描到的所有元件等都是作為BeanDefinition在Spring容器中的。

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

Spring解析xml時就會把一個<bean/>标簽解析成一個BeanDefinition執行個體,最終通過BeanDefintion在Spring容器中生成一個Bean。

6. BeanDefinitionRegistry 接口

BeanDefinitionRegistry 是一個 BeanDefinition 系統資料庫。

Spring Cloud OpenFeign 源碼解析(一)重要類與接口解析重要類與接口解析1. @EnableFeignClients2. @FeignClient3. FeignClientSpecification 類4. FeignContext 類5. BeanDefinition 接口6. BeanDefinitionRegistry 接口

繼續閱讀