Apollo 源码解析 —— 客户端配置 Spring 集成(三)之外部化配置

> 摘要: 原创出处 http://www.iocoder.cn/Apollo/client-config-spring-3/ 「芋道源码」欢迎转载,保留摘要,谢谢!

1. 概述

老艿艿:本系列假定胖友已经阅读过 《Apollo 官方 wiki 文档》 ,特别是 《Spring 整合方式》

😁 因为 Spring 仅仅处于入门水平,所以可能一些地方,表述的灰常业余。

本文分享 Spring 外部化配置的集成。我们先看看官方文档的说明:

使用上述两种方式的配置形式( 基于 XML 的配置和基于Java的配置 )后,Apollo 会在 Spring 的 postProcessBeanFactory 阶段注入配置到 Spring 的 Environment中,早于 bean 的初始化阶段,所以对于普通的 bean 注入配置场景已经能很好的满足。

不过 Spring Boot 有一些场景需要配置在更早的阶段注入,比如使用 @ConditionalOnProperty 的场景或者是有一些 spring-boot-starter 在启动阶段就需要读取配置做一些事情( 如 spring-boot-starter-dubbo ),所以对于 Spring Boot 环境建议通过以下方式来接入 Apollo ( 需要0.10.0及以上版本 )。
使用方式很简单,只需要在 application.properties/bootstrap.properties 中按照如下样例配置即可。

1、在 bootstrap 阶段注入默认 "application" namespace 的配置示例:

# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true

2、在 bootstrap 阶段注入非默认 "application" namespace 或多个 namespace 的配置示例

apollo.bootstrap.enabled = true
# will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
apollo.bootstrap.namespaces = application,FX.apollo

下面,让我们来看看具体的代码实现。

2. spring.factories

Apollo 在 apollo-clientMETA-INF/spring.factories 定义如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
  • 这个 spring.factories 里面配置的那些类,主要作用是告诉 Spring Boot 这个 starter 所需要加载的那些 xxxAutoConfiguration 和 xxxContextInitializer 类,也就是你真正的要自动注册的那些 bean 或功能。然后,我们实现一个 spring.factories 指定的类即可。
  • 此处配置了 ApolloAutoConfigurationApolloApplicationContextInitializer 类。

3. ApolloAutoConfiguration

com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration ,自动注入 ConfigPropertySourcesProcessor bean 对象,当不存在 PropertySourcesProcessor 时,以实现 Apollo 配置的自动加载。代码如下:

@Configuration
@ConditionalOnProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED)
@ConditionalOnMissingBean(PropertySourcesProcessor.class) // 缺失 PropertySourcesProcessor 时
public class ApolloAutoConfiguration {

    @Bean
    public ConfigPropertySourcesProcessor configPropertySourcesProcessor() {
        return new ConfigPropertySourcesProcessor(); // 注入 ConfigPropertySourcesProcessor bean 对象
    }

}

4. ApolloApplicationContextInitializer

com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer ,实现 ApplicationContextInitializer 接口,在 Spring Boot 启动阶段( bootstrap phase ),注入配置的 Apollo Config 对象们。

实现代码上,和 PropertySourcesProcessor 一样实现了注入配置的 Apollo Config 对象们,差别在于处于 Spring 的不同阶段。

代码如下:

  1: public class ApolloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  2:
  3:     private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
  4:     private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
  5:
  6:     private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class);
  7:
  8:     @Override
  9:     public void initialize(ConfigurableApplicationContext context) {
 10:         ConfigurableEnvironment environment = context.getEnvironment();
 11:         // 获得 "apollo.bootstrap.enabled" 配置项
 12:         String enabled = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "false");
 13:         // 忽略,若未开启
 14:         if (!Boolean.valueOf(enabled)) {
 15:             logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
 16:             return;
 17:         }
 18:         logger.debug("Apollo bootstrap config is enabled for context {}", context);
 19:
 20:         // 忽略,若已经有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 的 PropertySource
 21:         if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
 22:             // already initialized
 23:             return;
 24:         }
 25:
 26:         // 获得 "apollo.bootstrap.namespaces" 配置项
 27:         String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
 28:         logger.debug("Apollo bootstrap namespaces: {}", namespaces);
 29:         List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);
 30:
 31:         // 按照优先级,顺序遍历 Namespace
 32:         CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
 33:         for (String namespace : namespaceList) {
 34:             // 创建 Apollo Config 对象
 35:             Config config = ConfigService.getConfig(namespace);
 36:             // 创建 Namespace 对应的 ConfigPropertySource 对象
 37:             // 添加到 `composite` 中。
 38:             composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
 39:         }
 40:
 41:         // 添加到 `environment` 中,且优先级最高
 42:         environment.getPropertySources().addFirst(composite);
 43:     }
 44:
 45: }
  • 第 12 行:获得 "apollo.bootstrap.enabled" 配置项。
  • 第 13 至 18 行:忽略,若未配置开启。
  • 第 20 至 24 行:忽略,若已经有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 的 PropertySource 对象。
  • 第 26 至 29 行:获得 "apollo.bootstrap.namespaces" 配置项。
  • 第 31 至 33 行:按照顺序遍历 Namespace 。
    • 第 35 行:调用 ConfigService#getConfig(namespace) 方法,获得( 创建 ) Apollo Config 对象。这个地方,非常关键。
    • 第 38 行:调用 ConfigPropertySourceFactory#getConfigPropertySource(namespace, config) 方法,创建 Namespace 对应的 ConfigPropertySource 对象。
    • 第 38 行:调用 CompositePropertySource#addPropertySource(PropertySource) 方法,添加到 composite 中。通过这样的方式,形成顺序的优先级
    • 第 42 行:添加 compositeenvironment 中。这样,我们从 environment 里,且优先级最高

666. 彩蛋

完结,撒花。但是,好惆怅!!!

赞(0) 打赏

如未加特殊说明,此网站文章均为原创,转载必须注明出处。Java 技术驿站 » Apollo 源码解析 —— 客户端配置 Spring 集成(三)之外部化配置
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

关注【Java 技术驿站】公众号,每天早上 8:10 为你推送一篇技术文章

扫描二维码关注我!


关注【Java 技术驿站】公众号 回复 “VIP”,获取 VIP 地址永久关闭弹出窗口

免费获取资源

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏