表扬自己:《D数据库实体设计合集》
摘要: 原创出处 www.iocoder.cn/Apollo/clie… 「芋道源码」欢迎转载,保留摘要,谢谢!
🙂🙂🙂关注微信公众号:【芋道源码】有福利:
- RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
- RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
- 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
- 新的源码解析文章实时收到通知。每周更新一篇左右。
- 认真的源码交流微信群。
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 行:添加
composite
到environment
中。这样,我们从environment
里,且优先级最高。
- 第 35 行:调用
666. 彩蛋
完结,撒花。但是,好惆怅!!!