⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

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


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

阅读源码最好的方式,是使用 IDEA 进行调试 Apollo 源码,不然会一脸懵逼。

胖友可以点击「芋道源码」扫码关注,回复 git018 关键字
获得艿艿添加了中文注释的 Apollo 源码地址。

阅读源码很孤单,加入源码交流群,一起坚持!

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-client` 的 [`META-INF/spring.factories`](https://github.com/YunaiV/apollo/blob/2907eebd618825f32b8e27586cb521bcd0221a7e/apollo-client/src/main/resources/META-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` 指定的类即可。
* 此处配置了 **ApolloAutoConfiguration** 和 **ApolloApplicationContextInitializer** 类。

# 3. ApolloAutoConfiguration

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

```Java
@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. 彩蛋

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

知识星球

文章目录
  1. 1. 1. 概述
  2. 2. 4. ApolloApplicationContextInitializer
  3. 3. 666. 彩蛋