启动过程
"UML
"XML配置
NamespaceHandler
com.ctrip.framework.apollo.spring.config.NamespaceHandler
,实现 org.springframework.beans.factory.xml.NamespaceHandlerSupport
抽象类,Apollo 的 XML Namespace 的处理器。
如下是apollo namespace的xml配置:
1 | <!-- 这个是最简单的配置形式,一般应用用这种形式就可以了,用来指示Apollo注入application namespace的配置到Spring环境中 --> |
NamespaceHandler
把每一个带有config标签的元素解析成ConfigPropertySourcesProcessor
类型的bean,由spring管理,实现如下:
1 | public class NamespaceHandler extends NamespaceHandlerSupport { |
ConfigPropertySourcesProcessor
com.ctrip.framework.apollo.spring.config.ConfigPropertySourcesProcessor
,实现BeanDefinitionRegistryPostProcessor
接口,继承 PropertySourcesProcessor
类,Apollo PropertySource 处理器。
Apollo Property Sources processor for Spring XML Based Application
1 | public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor |
postProcessBeanDefinitionRegistry
在postProcessBeanDefinitionRegistry
阶段,DefaultConfigPropertySourcesProcessorHelper
注册一些BeanDefinition
到BeanDefinitionRegistry
中
1 | public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { |
Apollo 为了实现自动更新机制,做了很多处理,重点在于找到 XML 和注解配置的 PlaceHolder,全部以 StringValue
的形式,注册到 SpringValueRegistry
中,从而让 AutoUpdateConfigChangeListener
监听到 Apollo 配置变更后,能够从 SpringValueRegistry
中找到发生属性值变更的属性对应的 StringValue
,进行修改。
SpringValueDefinitionProcessor
com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor
,实现 BeanDefinitionRegistryPostProcessor 接口,处理 Spring XML PlaceHolder ,解析成 StringValueDefinition 集合。
SpringValueDefinition
1 | <bean class="com.ctrip.framework.apollo.demo.spring.xmlConfigDemo.bean.XmlBean"> |
每个 <property />
都会被解析成一个 StringValueDefinition 对象。
1 | public class SpringValueDefinition { |
eg: name值: timeout
映射成propertyName
,value值:${timeout:200}
映射成placeholder
,timeout
映射成key
postProcessBeanDefinitionRegistry
1 | private static final Map<BeanDefinitionRegistry, Multimap<String, SpringValueDefinition>> beanName2SpringValueDefinitions = |
PlaceholderHelper#extractPlaceholderKeys(placeholder)
方法,为什么会提取到多个 key
属性呢?
Extract keys from placeholder, e.g.
${some.key}
=> “some.key”${some.key:${some.other.key:100}}
=> “some.key”, “some.other.key”${${some.key}}
=> “some.key”${${some.key:other.key}}
=> “some.key”${${some.key}:${another.key}}
=> “some.key”, “another.key”#{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')}
=> “some.key”, “another.key”
PropertySourcesProcessor
com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor
, 实现 BeanFactoryPostProcessor、EnvironmentAware、PriorityOrdered 接口,PropertySource 处理器。同时也是ConfigPropertySourcesProcessor的父类。
postProcessBeanFactory
实现 BeanFactoryPostProcessor 接口,可以在 spring 的 bean 创建之前,修改 bean 的定义属性。也就是说,Spring 允许 BeanFactoryPostProcessor 在容器实例化任何其它bean 之前读取配置元数据,并可以根据需要进行修改,例如可以把 bean 的
scope
从 singleton 改为 prototype ,也可以把property
的值给修改掉。可以同时配置多个BeanFactoryPostProcessor ,并通过设置order
属性来控制各个BeanFactoryPostProcessor 的执行次序。注意:BeanFactoryPostProcessor 是在 spring 容器加载了 bean 的定义文件之后,在 bean 实例化之前执行的。
#postProcessBeanFactory(ConfigurableListableBeanFactory)
方法,代码如下:
1 |
|
初始化PropertySource
1 | private void initializePropertySources() { |
初始化AutoUpdateConfigChangeListener
调用 #initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory)
方法,初始化 AutoUpdateConfigChangeListener
对象,实现 Spring Placeholder 的自动更新功能。代码如下:
1 | private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) { |
ApolloProcessor
com.ctrip.framework.apollo.spring.annotation.ApolloProcessor
,实现 BeanPostProcessor、PriorityOrdered 接口,Apollo 处理器抽象类,封装了在 Spring Bean 初始化之前,处理属性和方法。但是具体的处理,是两个抽象方法:
1 | public abstract class ApolloProcessor implements BeanPostProcessor, PriorityOrdered { |
ApolloProcessor 有三个子类实现:
- SpringValueProcessor
- ApolloAnnotationProcessor
- ApolloJsonValueProcessor
SpringValueProcessor
com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor
,实现 BeanFactoryPostProcessor 接口,继承 ApolloProcessor 抽象类,Spring Value 处理器,处理:
- 带有
@Value
注解的 Field 和 Method - XML 配置的 Bean 的 PlaceHolder 们
每个 Field、Method、XML PlaceHolder 被处理成一个 SpringValue 对象,添加到 SpringValueRegistry 中。
目的还是,为了 PlaceHolder 的自动更新机制。
SpringValue
1 | public class SpringValue { |
SpringValueRegistry
SpringValue 注册表
1 | public class SpringValueRegistry { |
postProcessBeanFactory
1 | private Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions; |
SpringValueDefinitionProcessor的执行在postProcessBeanDefinitionRegistry阶段;SpringValueProcessor执行在postProcessBeanFactory阶段。SpringValueDefinitionProcessor执行在SpringValueProcessor前面。
postProcessBeforeInitialization
1 |
|
processField
1 |
|
processMethod
1 |
|
AutoUpdateConfigChangeListener
com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener
,实现 ConfigChangeListener 接口,自动更新配置监听器。
onChange
1 | public void onChange(ConfigChangeEvent changeEvent) { |
updateSpringValue
1 | private void updateSpringValue(SpringValue springValue) { |
java配置
@EnableApolloConfig
com.ctrip.framework.apollo.spring.annotation.@EnableApolloConfig
注解,可以使用它声明使用的 Apollo Namespace ,和 Apollo XML 配置的 <apollo:config />
等价。
1 | (RetentionPolicy.RUNTIME) |
ApolloConfigRegistrar
com.ctrip.framework.apollo.spring.annotation.ApolloConfigRegistrar
,实现 ImportBeanDefinitionRegistrar 接口,Apollo Spring Java Config 注册器。
ImportBeanDefinitionRegistrar:Spring官方在动态注册bean时,大部分套路其实是使用ImportBeanDefinitionRegistrar接口。所有实现了该接口的类的都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先与依赖其的bean初始化的,也能被aop、validator等机制处理。
1 | public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar { |
其他Annotation
@ApolloJsonValue
com.ctrip.framework.apollo.spring.annotation.@ApolloJsonValue
注解,将 Apollo 的一个 JSON 格式的属性进行注入,例如:
1 | // Inject the json property value for type SomeObject. |
将 Apollo 任意格式的 Namespace 的一个 Item 配置项,解析成对应类型的对象,注入到 @ApolloJsonValue
的对象中。
@ApolloConfig
com.ctrip.framework.apollo.spring.annotation.@ApolloConfig
注解,将 Apollo Config 对象注入,例如:
1 | // Inject the config for "someNamespace" |
@ApolloConfigChangeListener
com.ctrip.framework.apollo.spring.annotation.@ApolloConfigChangeListener
注解,将被注解的方法,向指定的 Apollo Config 发起配置变更监听,例如:
1 | // Listener on namespaces of "someNamespace" and "anotherNamespace" |
其他配置
使用上述两种方式的配置形式( 基于 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 的配置示例:
1 |
|
2、在 bootstrap 阶段注入非默认 "application"
namespace 或多个 namespace 的配置示例
1 | apollo.bootstrap.enabled = true |
下面,让我们来看看具体的代码实现。
spring.factories
Apollo 在 apollo-client
的 META-INF/spring.factories
定义如下:
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
ApolloAutoConfiguration
1 |
|
ApolloApplicationContextInitializer
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
,实现 ApplicationContextInitializer 接口,在 Spring Boot 启动阶段( bootstrap phase ),注入配置的 Apollo Config 对象们。
实现代码上,和 PropertySourcesProcessor 一样实现了注入配置的 Apollo Config 对象们,差别在于处于 Spring 的不同阶段。
1 |
|