Apollo 1 融合 Spring 的三個入口

莫那·魯道發表於2019-03-02

前言

Spring 作為 Java 世界非官方標準框架,任何一箇中介軟體想要得到良好的發展,必須完美支援 Spring 的各種特性,即:無縫融入 Spring。

Apollo 作為分散式配置中心,服務於 Java 應用程式,Java 應用程式可以通過 Apollo 提供的 Client 獲取遠端配置資訊。而如何將這個 Client 高度融合到使用者的應用程式中呢?

這就需要針對 Spring 提供給我們的介面進行擴充套件。

在之前的文章中,已經大致聊過 Spring 的一些擴充套件介面:深入理解Spring 之 Spring 進階開發必知必會 之 Spring 擴充套件介面

而想融入 Spring,首先得找到入口,然後才能註冊相關的類進行自己系統的初始化。
所以,如何找到並處理入口成了一門學問,我們今天看看 apollo 如何處理的。

第一種入口:XML

XML 是傳統 Java 專案的配置檔案,特別是 Spring MVC 專案。雖然現在都是使用的自動化配置,但仍然有一些遺留專案使用 XML,因此,支援 XML 是大部分中介軟體的必須工作。

支援 XML 需要準守 Spring 的幾個約定:

  1. 繼承 NamespaceHandlerSupport 抽象類,重寫 init 方法,呼叫 registerBeanDefinitionParser 方法,並傳入自己繼承的 AbstractSingleBeanDefinitionParser 子類用於解析標籤,重寫他的 getBeanClass 方法,返回一個 Bean,用於註冊相關的 Bean。
  2. classpath 下建立 META-INF 目錄,建立 spring.handlers 檔案,將 xml 配置中的 URL 指向 NamespaceHandlerSupport
  3. META-INF 目錄下,建立 apollo-1.0.0.xsd xsd 檔案,用於解釋自定義標籤。
  4. META-INF 目錄下,繼續建立 spring.schemas 檔案,將 xml 配置中的 xsd URL 指向 xsd 檔案。

如果你的 xml 配置中,引用了 apollo 的標籤,Spring 將會根據 xml 中的 URL 找到 spring.handlers 中的 NamespaceHandlerSupport 類,並對標籤進行解析。也會從 getBeanClass 得到一個設定的 bean,在這個 bean 裡,做了 apollo 關鍵類的註冊。

第二種入口:@Import 註解

相對於基於XML的配置,基於Java的配置是目前比較流行的方式。

@Import 註解的使用方式:

  1. 定義一個自己的啟動註解。並標註 @import 註解, 其實就是 xml 中的 import 標籤,在該註解中,可以配置一個類,這個類就會註冊進 Spring 的容器,成為 Bean,你就可以在這個 bean 裡做文章。
    在 apollo 中,使用方式如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
  String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
  int order() default Ordered.LOWEST_PRECEDENCE;
}
複製程式碼
  1. 從上面可以看出 ApolloConfigRegistrar 類是 apollo 註冊進的 bean。這個 bean 用於處理 @EnableApolloConfig 註解,同時註冊 apollo 關鍵 Bean 到 Spring 容器中。

  2. 使用者只需在 Spring 系統中的某個類上,標註 @EnableApolloConfig ,就可以通過 Spring 的方式(自動更新,註解等)使用 apollo 功能。

第三種入口:SpringBoot Starter

目前最流行的框架就是 Spring Boot ,相容 SpringBoot 是一個大趨勢。

Spring Boot 提供 spring-boot-autoconfigure 讓第三方框架相容 Boot,稱之為 starter。

建立一個 starter 需要遵守幾個約定:

  1. maven 引入 spring-boot-autoconfigure artifact.
  2. 建立一個類,實現ApplicationContextInitializer 介面,重寫 initialize 方法,該方法在容器初始化的時候呼叫。
  3. META-INF 建立 spring.factories 檔案,Boot 啟動時會自動掃描這個檔案。需要在這個檔案中寫入一個步驟 2 建立的類,類似 org.springframework.context.ApplicationContextInitializer=com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer。這個類的作用是提前(容器初始化前)載入關鍵配置到 Spring 環境。
  4. spring.factories 檔案中,還需要讓 boot 的 EnableAutoConfiguration 自動配置類指向一個自定義類。這是 SpringBoot starter 的關鍵,例如:org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration。ApolloAutoConfiguration 就會加入的 apollo 的配置 bean 中。你可以在這個配置 bean 中,建立一個關鍵 bean ,用於處理系統相關的初始化類。例如 apollo 的方式:
@Configuration
@ConditionalOnProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED)
@ConditionalOnMissingBean(PropertySourcesProcessor.class)// 當Spring Context中不存在該Bean時
public class ApolloAutoConfiguration {
  @Bean
  public ConfigPropertySourcesProcessor configPropertySourcesProcessor() {
    return new ConfigPropertySourcesProcessor();
  }
}
複製程式碼

在 apollo 中,ConfigPropertySourcesProcessor 就是用來註冊系統關鍵 bean 的。

總結

本文重點介紹了 3 種入口:

  1. XML 方式,通過在 getBeanClass 方法返回系統關鍵 Bean。
  2. @Import 註解,通過在註解中定義 Bean,然後在該 Bean 中處理。
  3. SpringBoot Starter 方式,通過 spring.factories 檔案中定義自動配置類,可以註冊系統關鍵 bean。

在以後的開發中,如果想融入 Spring,就可以通過這 3 種方式自行處理。

相關文章