SpringCloud升級之路2020.0.x版-9.如何理解並定製一個Spring Cloud組件

本系列為之前系列的整理重啟版,隨着項目的發展以及項目中的使用,之前系列裏面很多東西發生了變化,並且還有一些東西之前系列並沒有提到,所以重啟這個系列重新整理下,歡迎各位留言交流,謝謝!~

我們實現的 Spring Cloud 微服務框架,裏面運用了許多 Spring Cloud 組件,並且對於某些組件進行了個性化改造。那麼對於某個 Spring Cloud 組件,我們一般是如何入手理解其中的原理呢?以及如何知道其中的擴展點呢?一般從下面兩個方面入手:

  1. 通過 spring-boot SPI 機制查看模塊的擴展點
  2. 查看該模塊實現的 NamedContextFactory

spring-core 項目中提供了 Spring 框架多種 SPI 機制,其中一種非常常用並靈活運用在了 Spring-boot 的機制就是基於 spring.factories 的 SPI 機制。

那麼什麼是 SPI(Service Provider)呢? 在系統設計中,為了模塊間的協作,往往會設計統一的接口供模塊之間的調用。面向的對象的設計里,我們一般推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼,而是將指定哪個實現置於程序之外指定。Java 中默認的 SPI 機制就是通過 ServiceLoader 來實現,簡單來說就是通過在META-INF/services目錄下新建一個名稱為接口全限定名的文件,內容為接口實現類的全限定名,之後程序通過代碼:

//指定加載的接口類,以及用來加載類的類加載器,如果類加載器為 null 則用根類加載器加載
ServiceLoader<SpiService> serviceLoader = ServiceLoader.load(SpiService.class, someClassLoader);
Iterator<SpiService> iterator = serviceLoader.iterator();
while (iterator.hasNext()){
    SpiService spiService = iterator.next();
}

獲取指定的實現類。

在 Spring 框架中,這個類是SpringFactoriesLoader,需要在META-INF/spring.factories文件中指定接口以及對應的實現類,例如 Spring Cloud Commons 中的:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor

其中指定了EnvironmentPostProcessor的實現HostInfoEnvironmentPostProcessor

同時,Spring Boot 中會通過SpringFactoriesLoader.loadXXX類似的方法讀取所有的EnvironmentPostProcessor的實現類並生成 Bean 到 ApplicationContext 中:

EnvironmentPostProcessorApplicationListener

//這個類也是通過spring.factories中指定ApplicationListener的實現而實現加載的,這裡省略
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
    //創建這個Bean的時候,會調用
    public EnvironmentPostProcessorApplicationListener() {
		this(EnvironmentPostProcessorsFactory
				.fromSpringFactories(EnvironmentPostProcessorApplicationListener.class.getClassLoader()));
	}
}

EnvironmentPostProcessorsFactory

static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
	return new ReflectionEnvironmentPostProcessorsFactory(
	        //通過 SpringFactoriesLoader.loadFactoryNames 獲取文件中指定的實現類並初始化
			SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
}

META-INF/spring.factories 文件中不一定指定的是接口以及對應的實現類,例如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration,\
org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration,\

其中EnableAutoConfiguration是一個註解,LoadBalancerAutoConfigurationBlockingLoadBalancerClientAutoConfiguration都是配置類並不是EnableAutoConfiguration的實現。那麼這個是什麼意思呢?EnableAutoConfiguration是一個註解,LoadBalancerAutoConfigurationBlockingLoadBalancerClientAutoConfiguration都是配置類。spring.factories這裡是另一種特殊使用,記錄要載入的 Bean 類。EnableAutoConfiguration在註解被使用的時候,這些 Bean 會被加載。這就是spring.factories的另外一種用法。

EnableAutoConfiguration是 Spring-boot 自動裝載的核心註解。有了這個註解,Spring-boot 就可以自動加載各種@Configuration註解的類。那麼這個機制是如何實現的呢?

來看下EnableAutoConfiguration的源碼
EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	//排除的類
	Class<?>[] exclude() default {};
	//排除的Bean名稱
	String[] excludeName() default {};
}

我們看到了有 @Import 這個註解。這個註解是 Spring 框架的一個很常用的註解,是 Spring 基於 Java 註解配置的主要組成部分。

  1. 查看 jar 包的 META-INF/spring.factories
  2. 查看裏面的內容,尤其關注 org.springframework.boot.autoconfigure.EnableAutoConfiguration= 自動加載的配置類
  3. 查看自動加載的配置類,關注哪些 Bean 可以擴展(例如,包含@ConditionalOnMissingBean 註解的 Bean)

我們一般想個性化定製都是針對調用不同微服務不同的 Bean 配置,所以其實要重點關注的就是這個模塊擴展的 NamedContextFactory:

  1. 尋找這個組件擴展 NamedContextFactory 的類
  2. 查看類的源代碼,查看默認配置是什麼類,以及 Specification 是什麼類,以及如何獲取當前微服務的名稱。
  3. 根據默認配置類,查看裏面的 Bean 有哪些,並且哪些可以被替換(例如,包含@ConditionalOnMissingBean 註解的 Bean)
  4. 根據 Specification 查看擴展配置的方式

我們這裡拿 spring-cloud-loadbalancer 舉一個簡單例子,即:

spring-cloud-loadbalancer 中擴展 NamedContextFactory 的類是 LoadBalancerClientFactory,查看 LoadBalancerClientFactory 的代碼可以知道:

  1. 可以通過 loadbalancer.client.name 這個屬性獲取當前要創建的 Bean 是哪個微服務的
  2. 可以知道默認配置是 LoadBalancerClientConfiguration,再查看它裏面的源代碼我們可以知道主要初始化兩個 Bean:
    1. ReactorLoadBalancer,負載均衡器,因為有 @ConditionalOnMissingBean 所以可以被替換,這就是我們的擴展點
    2. ServiceInstanceSupplier,提供實例信息的 Supplier,因為有 @ConditionalOnMissingBean 所以可以被替換,這就是我們的擴展點
  3. Specification 為 LoadBalancerSpecification,再分析其調用可以知道,可以通過 @LoadBalancerClient@LoadBalancerClientsLoadBalancerClientConfiguration 的基礎上額外指定配置。

我們這一節詳細分析了如何使用以及分析改造一個 Spring Cloud 組件。下一節我們將開始具體分析我們實現的微服務框架的每一塊功能。

微信搜索「我的編程喵」關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer