1. 概述
老艿艿:本系列假定胖友已经阅读过 《Apollo 官方 wiki 文档》 ,特别是 《Spring 整合方式》 。
😁 因为 Spring 仅仅处于入门水平,所以可能一些地方,表述的灰常业余。
从本文开始,我们来看看 Apollo 客户端的配置,是如何和 Spring 集成的。笔者会分成三篇文章,分别是:
- XML 配置 【本文】
- 注解配置
- 外部化配置
其中,注解配置会包含 Java Config ,实在不好拆分 😓 。推荐一篇有趣的文章,《用小说的形式讲解Spring(3) —— xml、注解和Java Config到底选哪个》 。
本文分享 Spring XML 配置的集成,包括两方面:
- Apollo Config 集成到 Spring PropertySource 体系中。
- 自动更新 Spring Placeholder Values ,参见 PR #972 。
Apollo XML 配置,自定义 <apollo: />
标签,基于 Spring XML 进行解析。如果不了解的胖友,可以查看如下文档:
2. 定义
2.1 spring.schemas
Apollo 在 apollo-client
的 META-INF/spring.schemas
定义如下:
http\://www.ctrip.com/schema/apollo-1.0.0.xsd=/META-INF/apollo-1.0.0.xsd
http\://www.ctrip.com/schema/apollo.xsd=/META-INF/apollo-1.0.0.xsd
xmlns
为http://code.alibabatech.com/schema/dubbo/dubbo.xsd
或http://code.alibabatech.com/schema/dubbo/dubbo-1.0.0.xsd
。xsd
为META-INF/apollo-1.0.0.xsd
。
2.2 apollo-1.0.0.xsd
apollo-1.0.0.xsd
定义如下:
定义了 <apollo:config />
标签,可对应多个 Config 对象。
namespaces
属性,Namespace 名字的数组,这就是为什么说“多个”的原因。order
属性,多个 Namespace 可能存在多个相同属性名的属性,应用程序在使用属性时,是不知道 Namespace 的存在的,所以需要通过order
来判断优先级。order
越小,优先级越高。
2.3 spring.handlers
spring.handlers
定义如下:
http\://www.ctrip.com/schema/apollo=com.ctrip.framework.apollo.spring.config.NamespaceHandler
- 定义了 Apollo 的 XML Namespace 的处理器 NamespaceHandler 。
2.4 NamespaceHandler
com.ctrip.framework.apollo.spring.config.NamespaceHandler
,实现 org.springframework.beans.factory.xml.NamespaceHandlerSupport
抽象类,Apollo 的 XML Namespace 的处理器。代码如下
public class NamespaceHandler extends NamespaceHandlerSupport {
private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
@Override
public void init() {
registerBeanDefinitionParser("config", new BeanParser());
}
static class BeanParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return ConfigPropertySourcesProcessor.class;
}
@Override
protected boolean shouldGenerateId() {
return true;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
// 解析 `namespaces` 属性,默认为 `"application"`
String namespaces = element.getAttribute("namespaces");
// default to application
if (Strings.isNullOrEmpty(namespaces)) {
namespaces = ConfigConsts.NAMESPACE_APPLICATION;
}
// 解析 `order` 属性,默认为 Ordered.LOWEST_PRECEDENCE;
int order = Ordered.LOWEST_PRECEDENCE;
String orderAttribute = element.getAttribute("order");
if (!Strings.isNullOrEmpty(orderAttribute)) {
try {
order = Integer.parseInt(orderAttribute);
} catch (Throwable ex) {
throw new IllegalArgumentException(
String.format("Invalid order: %s for namespaces: %s", orderAttribute, namespaces));
}
}
// 添加到 PropertySourcesProcessor
PropertySourcesProcessor.addNamespaces(NAMESPACE_SPLITTER.splitToList(namespaces), order);
}
}
}
#getBeanClass(Element)
方法,返回 Bean 对应的类为 ConfigPropertySourcesProcesso。详细解析,见 「4. ConfigPropertySourcesProcesso」 。#doParse(Element, BeanDefinitionBuilder)
方法,解析 XML 配置。- 解析
namespaces
和order
属性。 - 调用
PropertySourcesProcessor#addNamespaces(namespaces, order)
方法,添加到 PropertySourcesProcessor 中。什么用?我们接着往下看。
- 解析
3. PropertySourcesProcessor
com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor
,实现 BeanFactoryPostProcessor、EnvironmentAware、PriorityOrdered 接口,PropertySource 处理器。
The reason why PropertySourcesProcessor implements BeanFactoryPostProcessor instead of
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
is that lower versions of Spring (e.g. 3.1.1) doesn't support registering BeanDefinitionRegistryPostProcessor in ImportBeanDefinitionRegistrar -com.ctrip.framework.apollo.spring.annotation.ApolloConfigRegistrar
。
3.1 构造方法
/**
* Namespace 名字集合
*
* KEY:优先级
* VALUE:Namespace 名字集合
*/
private static final Multimap<Integer, String> NAMESPACE_NAMES = LinkedHashMultimap.create();
/**
* 是否初始化的标识
*/
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false);
private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector.getInstance(ConfigPropertySourceFactory.class);
private final ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
/**
* Spring ConfigurableEnvironment 对象
*/
private ConfigurableEnvironment environment;
NAMESPACE_NAMES
静态属性,Namespace 名字集合。Apollo 在解析到的 XML 或注解配置的 Apollo Namespace 时,会调用 #addNamespaces(namespaces, order)
方法,添加到其中。代码如下:
public static boolean addNamespaces(Collection<String> namespaces, int order) {
return NAMESPACE_NAMES.putAll(order, namespaces);
}
-
INITIALIZED
静态属性,是否初始化的标识。 -
environment
属性,Spring ConfigurableEnvironment 对象,通过它,可以获取到应用实例中,所有的配置属性信息。通过#setEnvironment(Environment)
方法,注入。代码如下:
@Override
public void setEnvironment(Environment environment) {
//it is safe enough to cast as all known environment is derived from ConfigurableEnvironment
this.environment = (ConfigurableEnvironment) environment;
}
-
configPropertySourceFactory
属性,ConfigPropertySource 工厂。在NAMESPACE_NAMES
中的每一个 Namespace ,都会创建成对应的 ConfigPropertySource 对象( 基于 Apollo Config 的 PropertySource 实现类 ),并添加到environment
中。重点:通过这样的方式,Spring 的<property name="" value="" />
和@Value
注解,就可以从environment
中,直接读取到对应的属性值。
胖友,先跳到 「5. ConfigPropertySource」 ,理解了 ConfigPropertySource 属性源,在回过头继续往下看。
3.2 postProcessBeanFactory
实现 BeanFactoryPostProcessor 接口,可以在 spring 的 bean 创建之前,修改 bean 的定义属性。也就是说,Spring 允许 BeanFactoryPostProcessor 在容器实例化任何其它bean 之前读取配置元数据,并可以根据需要进行修改,例如可以把 bean 的
scope
从 singleton 改为 prototype ,也可以把property
的值给修改掉。可以同时配置多个BeanFactoryPostProcessor ,并通过设置order
属性来控制各个BeanFactoryPostProcessor 的执行次序。注意:BeanFactoryPostProcessor 是在 spring 容器加载了 bean 的定义文件之后,在 bean 实例化之前执行的。
#postProcessBeanFactory(ConfigurableListableBeanFactory)
方法,代码如下:
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (INITIALIZED.compareAndSet(false, true)) {
// 初始化 PropertySource 们
initializePropertySources();
// 初始化 AutoUpdateConfigChangeListener 对象,实现属性的自动更新
initializeAutoUpdatePropertiesFeature(beanFactory);
}
}
- 调用
#initializePropertySources()
方法,初始化 PropertySource 们。代码如下:
1: private void initializePropertySources() {
2: // 若 `environment` 已经有 APOLLO_PROPERTY_SOURCE_NAME 属性源,说明已经初始化,直接返回。
3: if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME)) {
4: // already initialized
5: return;
6: }
7: // 创建 CompositePropertySource 对象,组合多个 Namespace 的 ConfigPropertySource 。
8: CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
9: // 按照优先级,顺序遍历 Namespace
10: // sort by order asc
11: ImmutableSortedSet<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
12: for (Integer order : orders) {
13: for (String namespace : NAMESPACE_NAMES.get(order)) {
14: // 创建 Apollo Config 对象
15: Config config = ConfigService.getConfig(namespace);
16: // 创建 Namespace 对应的 ConfigPropertySource 对象
17: // 添加到 `composite` 中。
18: composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
19: }
20: }
21:
22: // 若有 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 属性源,添加到其后
23: // add after the bootstrap property source or to the first
24: if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
25: environment.getPropertySources().addAfter(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME, composite);
26: // 若没 APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME 属性源,添加到首个
27: } else {
28: environment.getPropertySources().addFirst(composite);
29: }
30: }
-
- 第 2 至 6 行:
environment
已经有 APOLLO_PROPERTY_SOURCE_NAME 属性源,说明已经初始化,直接返回。本方法初始化的就是 APOLLO_PROPERTY_SOURCE_NAME 属性源。 - 第 8 行:创建 CompositePropertySource 对象,组合多个 Namespace 的 ConfigPropertySource 。
- 第 9 至 13 行:按照优先级,顺序遍历 Namespace 。
- 第 15 行:调用
ConfigService#getConfig(namespace)
方法,获得( 创建 ) Apollo Config 对象。这个地方,非常关键。 - 第 18 行:调用
ConfigPropertySourceFactory#getConfigPropertySource(namespace, config)
方法,创建 Namespace 对应的 ConfigPropertySource 对象。 - 第 18 行:调用
CompositePropertySource#addPropertySource(PropertySource)
方法,添加到composite
中。通过这样的方式,形成顺序的优先级。
- 第 15 行:调用
- 第 22 至 29 行:添加
composite
到environment
中。这样,我们从environment
里,可以读取到 Apollo Config 的配置。🙂 属性源的优先级为:APOLLO_BOOTSTRAPPROPERTY_SOURCE_NAME > APOLLO_PROPERTY_SOURCE_NAME > 其他。APOLLOBOOTSTRAP_PROPERTY_SOURCE_NAME 是外部化配置产生的 PropertySource 对象,优先级最高。- 😏 至此,Apollo Config 集成到 Spring XML 的 Bean 的属性,可以说已经完成了。如果不考虑,我们下面继续分享的自动更新功能。
- 第 2 至 6 行:
-
调用
#initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory)
方法,初始化 AutoUpdateConfigChangeListener 对象,实现 Spring Placeholder 的自动更新功能。代码如下:
private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
// 若未开启属性的自动更新功能,直接返回
if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
return;
}
// 创建 AutoUpdateConfigChangeListener 对象
AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(environment, beanFactory);
// 循环,向 ConfigPropertySource 注册配置变更器
List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
for (ConfigPropertySource configPropertySource : configPropertySources) {
configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
}
}
- 调用
ConfigUtil#isAutoUpdateInjectedSpringPropertiesEnabled()
方法,判断Spring Placeholder 的自动更新的功能是否开启。代码如下:
private boolean autoUpdateInjectedSpringProperties = true;
private void initAutoUpdateInjectedSpringProperties() {
// 1. Get from System Property
String enableAutoUpdate = System.getProperty("apollo.autoUpdateInjectedSpringProperties");
if (Strings.isNullOrEmpty(enableAutoUpdate)) {
// 2. Get from app.properties
enableAutoUpdate = Foundation.app().getProperty("apollo.autoUpdateInjectedSpringProperties", null);
}
if (!Strings.isNullOrEmpty(enableAutoUpdate)) {
autoUpdateInjectedSpringProperties = Boolean.parseBoolean(enableAutoUpdate.trim());
}
}
-
-
- 默认开启,可通过
-Dapollo.autoUpdateInjectedSpringProperties=false
或者在app.properties
中设置apollo.autoUpdateInjectedSpringProperties=false
进行关闭。
- 默认开启,可通过
-
创建 AutoUpdateConfigChangeListener 对象,并循环,向 ConfigPropertySource 注册为配置变更监听器,从而实现 Apollo Config 配置变更的监听。详细解析,见 「9. AutoUpdateConfigChangeListener」 。
-
3.3 getOrder
@Override
public int getOrder() {
// make it as early as possible
return Ordered.HIGHEST_PRECEDENCE; // 最高优先级
}
4. ConfigPropertySourcesProcessor
com.ctrip.framework.apollo.spring.config.ConfigPropertySourcesProcessor
,实现 BeanDefinitionRegistryPostProcessor 接口,继承 「3. PropertySourcesProcessor」 类,Apollo PropertySource 处理器。
Apollo Property Sources processor for Spring XML Based Application
- 用于处理 Spring XML 的配置。
4.1 postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 注册 PropertySourcesPlaceholderConfigurer 到 BeanDefinitionRegistry 中,替换 PlaceHolder 为对应的属性值,参考文章 https://leokongwq.github.io/2016/12/28/spring-PropertyPlaceholderConfigurer.html
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(), PropertySourcesPlaceholderConfigurer.class);
// 注册 ApolloAnnotationProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在 @ApolloConfig 和 @ApolloConfigChangeListener 注解。
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(), ApolloAnnotationProcessor.class);
// 注册 SpringValueProcessor 到 BeanDefinitionRegistry 中,用于 PlaceHolder 自动更新机制
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);
// 注册 ApolloJsonValueProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在 @ApolloJsonValue 注解。
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(), ApolloJsonValueProcessor.class);
// 处理 XML 配置的 Spring PlaceHolder
processSpringValueDefinition(registry);
}
- 调用
BeanRegistrationUtil#registerBeanDefinitionIfNotExists(registry, beanName, beanClass)
方法,注册beanClass
到 BeanDefinitionRegistry 中,当且仅当beanName
和beanClass
都不存在对应的 BeanDefinition 时。代码如下:
public static boolean registerBeanDefinitionIfNotExists(BeanDefinitionRegistry registry, String beanName, Class<?> beanClass) {
// 不存在 `beanName` 对应的 BeanDefinition
if (registry.containsBeanDefinition(beanName)) {
return false;
}
// 不存在 `beanClass` 对应的 BeanDefinition
String[] candidates = registry.getBeanDefinitionNames();
for (String candidate : candidates) {
BeanDefinition beanDefinition = registry.getBeanDefinition(candidate);
if (Objects.equals(beanDefinition.getBeanClassName(), beanClass.getName())) {
return false;
}
}
// 注册 `beanClass` 到 BeanDefinitionRegistry 中
BeanDefinition annotationProcessor = BeanDefinitionBuilder.genericBeanDefinition(beanClass).getBeanDefinition();
registry.registerBeanDefinition(beanName, annotationProcessor);
return true;
}
-
注册 PropertySourcesPlaceholderConfigurer 到 BeanDefinitionRegistry 中,替换 PlaceHolder 为对应的属性值,参考文章 《Spring 扩展点之 PropertyPlaceholderConfigurer》 。
-
注册 ApolloAnnotationProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在
@ApolloConfig
和@ApolloConfigChangeListener
注解。详细解析,见 《Apollo 源码解析 —— 客户端配置 Spring 集成(一)之注解配置》 。 -
注册 SpringValueProcessor 到 BeanDefinitionRegistry 中,用于 PlaceHolder 自动更新机制。详细解析,见 「7.1 SpringValueProcessor」 。
-
注册 ApolloJsonValueProcessor 到 BeanDefinitionRegistry 中,因为 XML 配置的 Bean 对象,也可能存在
@ApolloJsonValue
注解。详细解析,见 《Apollo 源码解析 —— 客户端配置 Spring 集成(一)之注解配置》 。 -
========== 分割线 ==========
-
调用
#processSpringValueDefinition(BeanDefinitionRegistry)
方法,处理 XML 配置的 Spring PlaceHolder 。这块的目的,也是用于 PlaceHolder 自动更新机制。
!!!划重点!!!
🙂 Apollo 为了实现自动更新机制,做了很多处理,重点在于找到 XML 和注解配置的 PlaceHolder,全部以 StringValue 的形式,注册到 SpringValueRegistry 中,从而让 AutoUpdateConfigChangeListener 监听到 Apollo 配置变更后,能够从 SpringValueRegistry 中找到发生属性值变更的属性对应的 StringValue ,进行修改。😈 相对复杂,其实也非常好理解,胖友看完所有的代码之后,可以在回味思考下。
4.2 processSpringValueDefinition
private void processSpringValueDefinition(BeanDefinitionRegistry registry) {
// 创建 SpringValueDefinitionProcessor 对象
SpringValueDefinitionProcessor springValueDefinitionProcessor = new SpringValueDefinitionProcessor();
// 处理 XML 配置的 Spring PlaceHolder
springValueDefinitionProcessor.postProcessBeanDefinitionRegistry(registry);
}
-
SpringValueDefinitionProcessor 的详细解析,见 「6. SpringValueDefinitionProcessor」 中。
-
该方法上有注释如下:
/**
* For Spring 3.x versions, the BeanDefinitionRegistryPostProcessor would not be
* instantiated if it is added in postProcessBeanDefinitionRegistry phase, so we have to manually
* call the postProcessBeanDefinitionRegistry method of SpringValueDefinitionProcessor here...
*/
-
- 在 Spring 3.x 版本中,BeanDefinitionRegistryPostProcessor ( SpringValueDefinitionProcessor 实现了该接口 )无法被实例化,在 postProcessBeanDefinitionRegistry 阶段,因此,我们不得不手动创建 SpringValueDefinitionProcessor 对象,并调用其
#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)
方法。 - 🙂 哈哈哈,不是很理解 Spring 的 Bean 的初始化的生命周期,所以胖友需要自己在查查。
- 在 Spring 3.x 版本中,BeanDefinitionRegistryPostProcessor ( SpringValueDefinitionProcessor 实现了该接口 )无法被实例化,在 postProcessBeanDefinitionRegistry 阶段,因此,我们不得不手动创建 SpringValueDefinitionProcessor 对象,并调用其
5. ConfigPropertySource
com.ctrip.framework.apollo.spring.config.ConfigPropertySource
,实现 org.springframework.core.env.ConfigPropertySource
抽象类,基于 Apollo Config 的 PropertySource 实现类。代码如下:
public class ConfigPropertySource extends EnumerablePropertySource<Config> {
private static final String[] EMPTY_ARRAY = new String[0];
ConfigPropertySource(String name, Config source) { // 此处的 Apollo Config 作为 `source`
super(name, source);
}
@Override
public String[] getPropertyNames() {
// 从 Config 中,获得属性名集合
Set<String> propertyNames = this.source.getPropertyNames();
// 转换成 String 数组,返回
if (propertyNames.isEmpty()) {
return EMPTY_ARRAY;
}
return propertyNames.toArray(new String[0]);
}
@Override
public Object getProperty(String name) {
return this.source.getProperty(name, null);
}
/**
* 添加 ConfigChangeListener 到 Config 中
*
* @param listener 监听器
*/
public void addChangeListener(ConfigChangeListener listener) {
this.source.addChangeListener(listener);
}
}
- 简单的将 Apollo Config 的方法,进行封装。
5.1 ConfigPropertySourceFactory
com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory
,ConfigPropertySource 工厂。代码如下:
public class ConfigPropertySourceFactory {
/**
* ConfigPropertySource 数组
*/
private final List<ConfigPropertySource> configPropertySources = Lists.newLinkedList();
// 创建 ConfigPropertySource 对象
public ConfigPropertySource getConfigPropertySource(String name, Config source) {
// 创建 ConfigPropertySource 对象
ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source);
// 添加到数组中
configPropertySources.add(configPropertySource);
return configPropertySource;
}
public List<ConfigPropertySource> getAllConfigPropertySources() {
return Lists.newLinkedList(configPropertySources);
}
}
6. SpringValueDefinitionProcessor
com.ctrip.framework.apollo.spring.property.SpringValueDefinitionProcessor
,实现 BeanDefinitionRegistryPostProcessor 接口,处理 Spring XML PlaceHolder ,解析成 StringValueDefinition 集合。例如:
<bean class="com.ctrip.framework.apollo.demo.spring.xmlConfigDemo.bean.XmlBean">
<property name="timeout" value="${timeout:200}"/>
<property name="batch" value="${batch:100}"/>
</bean>
- 每个
<property />
都会被解析成一个 StringValueDefinition 对象。关于 StringValueDefinition ,胖友先跳到 「8.1 StringValueDefinition」 。 - 对应关系如下图:
6.1 构造方法
/**
* SpringValueDefinition 集合
*
* KEY:beanName
* VALUE:SpringValueDefinition 集合
*/
private static final Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions = LinkedListMultimap.create();
/**
* 是否初始化的标识
*/
private static final AtomicBoolean initialized = new AtomicBoolean(false);
private final ConfigUtil configUtil;
private final PlaceholderHelper placeholderHelper;
public SpringValueDefinitionProcessor() {
configUtil = ApolloInjector.getInstance(ConfigUtil.class);
placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
}
beanName2SpringValueDefinitions
静态字段,SpringValueDefinition 集合。其中,KEY 为beanName
,VALUE 为 SpringValueDefinition 集合。
6.2 postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 是否开启自动更新功能,因为 SpringValueDefinitionProcessor 就是为了这个功能编写的。
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
processPropertyValues(registry);
}
}
private void processPropertyValues(BeanDefinitionRegistry beanRegistry) {
// 若已经初始化,直接返回
if (!initialized.compareAndSet(false, true)) {
// already initialized
return;
}
// 循环 BeanDefinition 集合
String[] beanNames = beanRegistry.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName);
// 循环 BeanDefinition 的 PropertyValue 数组
MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
List<PropertyValue> propertyValues = mutablePropertyValues.getPropertyValueList();
for (PropertyValue propertyValue : propertyValues) {
// 获得 `value` 属性。
Object value = propertyValue.getValue();
// 忽略非 Spring PlaceHolder 的 `value` 属性。
if (!(value instanceof TypedStringValue)) {
continue;
}
// 获得 `placeholder` 属性。
String placeholder = ((TypedStringValue) value).getValue();
// 提取 `keys` 属性们。
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
if (keys.isEmpty()) {
continue;
}
// 循环 `keys` ,创建对应的 SpringValueDefinition 对象,并添加到 `beanName2SpringValueDefinitions` 中。
for (String key : keys) {
beanName2SpringValueDefinitions.put(beanName, new SpringValueDefinition(key, placeholder, propertyValue.getName()));
}
}
}
}
-
代码比较简单,胖友看下注释。
-
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"
- 具体的代码实现,感兴趣的胖友,自己研究下。
7. ApolloProcessor
com.ctrip.framework.apollo.spring.annotation.ApolloProcessor
,实现 BeanPostProcessor、PriorityOrdered 接口,Apollo 处理器抽象类,封装了在 Spring Bean 初始化之前,处理属性和方法。但是具体的处理,是两个抽象方法:
/**
* subclass should implement this method to process field
*/
protected abstract void processField(Object bean, String beanName, Field field);
/**
* subclass should implement this method to process method
*/
protected abstract void processMethod(Object bean, String beanName, Method method);
1. postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class clazz = bean.getClass();
// 处理所有 Field
for (Field field : findAllField(clazz)) {
processField(bean, beanName, field);
}
// 处理所有的 Method
for (Method method : findAllMethod(clazz)) {
processMethod(bean, beanName, method);
}
return bean;
}
- 调用
#findAllField(Class)
方法,获得所有 Field 。代码如下:
private List<Field> findAllField(Class clazz) {
final List<Field> res = new LinkedList<>();
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
res.add(field);
}
});
return res;
}
- 调用
#findAllMethod(Class)
方法,获得所有 Method 。代码如下:
private List<Method> findAllMethod(Class clazz) {
final List<Method> res = new LinkedList<>();
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
res.add(method);
}
});
return res;
}
2. postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
3. getOrder
@Override
public int getOrder() {
// make it as late as possible
return Ordered.LOWEST_PRECEDENCE; // 最高优先级
}
4. 子类
ApolloProcessor 有三个子类实现:
- SpringValueProcessor
- ApolloJsonValueProcessor
- ApolloJsonValueProcessor
分文仅分享第一个,SpringValueProcessor ,和本文相关。
7.1 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 的自动更新机制。
7.1.1 构造方法
/**
* SpringValueDefinition 集合
*
* KEY:beanName
* VALUE:SpringValueDefinition 集合
*/
private static Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions = LinkedListMultimap.create();
private final ConfigUtil configUtil;
private final PlaceholderHelper placeholderHelper;
/**
* SpringValueRegistry 对象
*/
private final SpringValueRegistry springValueRegistry;
public SpringValueProcessor() {
configUtil = ApolloInjector.getInstance(ConfigUtil.class);
placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
}
beanName2SpringValueDefinitions
字段,SpringValueDefinition 集合。通过#postProcessBeanFactory()
方法,进行初始化。代码如下:
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 是否开启自动更新机制
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
beanName2SpringValueDefinitions = SpringValueDefinitionProcessor.getBeanName2SpringValueDefinitions();
}
}
-
- 因为需要在 「6. SpringValueDefinitionProcessor」 的
#postProcessBeanFactory(beanFactory)
方法之后。
- 因为需要在 「6. SpringValueDefinitionProcessor」 的
-
springValueRegistry
字段,SpringValue 注册表。胖友先跳到 「8. SpringValueRegistry」 ,看完在回来。
7.1.2 postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 是否开启自动更新机制
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
// 处理 Field 和 Method
super.postProcessBeforeInitialization(bean, beanName);
// 处理 XML 配置的 Bean 的 PlaceHolder 们
processBeanPropertyValues(bean, beanName);
}
return bean;
}
- 调用父
#postProcessBeforeInitialization(bean, beanName)
方法,处理 Field 和 Method 。 - 调用
#processBeanPropertyValues(bean, beanName)
方法,处理 XML 配置的 Bean 的 PlaceHolder 们。
7.1.3 processField
@Override
protected void processField(Object bean, String beanName, Field field) {
// register @Value on field
Value value = field.getAnnotation(Value.class);
if (value == null) {
return;
}
// 提取 `keys` 属性们。
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
if (keys.isEmpty()) {
return;
}
// 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
springValueRegistry.register(key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
- 从代码上,和 SpringValueDefinitionProcessor 的
#processPropertyValues(beanRegistry)
类似。
7.1.4 processMethod
@Override
protected void processMethod(Object bean, String beanName, Method method) {
// register @Value on method
Value value = method.getAnnotation(Value.class);
if (value == null) {
return;
}
// 忽略 @Bean 注解的方法
// skip Configuration bean methods
if (method.getAnnotation(Bean.class) != null) {
return;
}
// 忽略非 setting 方法
if (method.getParameterTypes().length != 1) {
logger.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters", bean.getClass().getName(), method.getName(), method.getParameterTypes().length);
return;
}
// 提取 `keys` 属性们。
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
if (keys.isEmpty()) {
return;
}
// 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
springValueRegistry.register(key, springValue);
logger.info("Monitoring {}", springValue);
}
}
- 从代码上,和 SpringValueDefinitionProcessor 的
#processPropertyValues(beanRegistry)
类似。
7.1.5 processBeanPropertyValues
private void processBeanPropertyValues(Object bean, String beanName) {
// 获得 SpringValueDefinition 数组
Collection<SpringValueDefinition> propertySpringValues = beanName2SpringValueDefinitions.get(beanName);
if (propertySpringValues == null || propertySpringValues.isEmpty()) {
return;
}
// 循环 SpringValueDefinition 数组,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。
for (SpringValueDefinition definition : propertySpringValues) {
try {
PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(bean.getClass(), definition.getPropertyName());
Method method = pd.getWriteMethod();
if (method == null) {
continue;
}
SpringValue springValue = new SpringValue(definition.getKey(), definition.getPlaceholder(), bean, beanName, method, false);
springValueRegistry.register(definition.getKey(), springValue);
logger.debug("Monitoring {}", springValue);
} catch (Throwable ex) {
logger.error("Failed to enable auto update feature for {}.{}", bean.getClass(), definition.getPropertyName());
}
}
// clear
// 移除 Bean 对应的 SpringValueDefinition 数组
beanName2SpringValueDefinitions.removeAll(beanName);
}
8. SpringValueRegistry
com.ctrip.framework.apollo.spring.property.SpringValueRegistry
,SpringValue 注册表。代码如下:
public class SpringValueRegistry {
/**
* SpringValue 集合
*
* KEY:属性 KEY ,即 Config 配置 KEY
* VALUE:SpringValue 数组
*/
private final Multimap<String, SpringValue> registry = LinkedListMultimap.create();
// 注册
public void register(String key, SpringValue springValue) {
registry.put(key, springValue);
}
// 获得
public Collection<SpringValue> get(String key) {
return registry.get(key);
}
}
8.1 SpringValue
com.ctrip.framework.apollo.spring.property.SpringValue
,代码如下:
public class SpringValue {
/**
* Bean 对象
*/
private Object bean;
/**
* Bean 名字
*/
private String beanName;
/**
* Spring 方法参数封装
*/
private MethodParameter methodParameter;
/**
* Field
*/
private Field field;
/**
* KEY
*
* 即在 Config 中的属性 KEY 。
*/
private String key;
/**
* 占位符
*/
private String placeholder;
/**
* 值类型
*/
private Class<?> targetType;
/**
* 是否 JSON
*/
private boolean isJson;
/**
* 泛型。当是 JSON 类型时,使用
*/
private Type genericType;
}
- 分成两种类型:Field 和 Method 类型。
8.1.1 构造方法
Field 和 Method 分别对应不同的构造方法:
// Field
public SpringValue(String key, String placeholder, Object bean, String beanName, Field field, boolean isJson) {
this.bean = bean;
this.beanName = beanName;
// Field
this.field = field;
this.key = key;
this.placeholder = placeholder;
// Field 差异
this.targetType = field.getType();
this.isJson = isJson;
if (isJson) {
this.genericType = field.getGenericType();
}
}
// Method
public SpringValue(String key, String placeholder, Object bean, String beanName, Method method, boolean isJson) {
this.bean = bean;
this.beanName = beanName;
// Method
this.methodParameter = new MethodParameter(method, 0);
this.key = key;
this.placeholder = placeholder;
// Method 差异
Class<?>[] paramTps = method.getParameterTypes();
this.targetType = paramTps[0];
this.isJson = isJson;
if (isJson) {
this.genericType = method.getGenericParameterTypes()[0];
}
}
8.1.2 更新
Field 和 Method 分别对应不同的更新方法:
public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {
// Field
if (isField()) {
injectField(newVal);
// Method
} else {
injectMethod(newVal);
}
}
private void injectField(Object newVal) throws IllegalAccessException {
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.set(bean, newVal);
field.setAccessible(accessible);
}
private void injectMethod(Object newVal) throws InvocationTargetException, IllegalAccessException {
methodParameter.getMethod().invoke(bean, newVal);
}
8.2 SpringValueDefinition
com.ctrip.framework.apollo.spring.property.SpringValueDefinition
,SpringValue 定义。代码如下:
/**
* KEY
*
* 即在 Config 中的属性 KEY 。
*/
private final String key;
/**
* 占位符
*/
private final String placeholder;
/**
* 属性名
*/
private final String propertyName;
9. AutoUpdateConfigChangeListener
com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener
,实现 ConfigChangeListener 接口,自动更新配置监听器。
9.1 构造方法
/**
* {@link TypeConverter#convertIfNecessary(Object, Class, Field)} 是否带上 Field 参数,因为 Spring 3.2.0+ 才有该方法
*/
private final boolean typeConverterHasConvertIfNecessaryWithFieldParameter;
private final Environment environment;
private final ConfigurableBeanFactory beanFactory;
/**
* TypeConverter 对象,参见 https://blog.csdn.net/rulerp2014/article/details/51100857
*/
private final TypeConverter typeConverter;
private final PlaceholderHelper placeholderHelper;
private final SpringValueRegistry springValueRegistry;
private final Gson gson;
public AutoUpdateConfigChangeListener(Environment environment, ConfigurableListableBeanFactory beanFactory) {
this.typeConverterHasConvertIfNecessaryWithFieldParameter = testTypeConverterHasConvertIfNecessaryWithFieldParameter();
this.beanFactory = beanFactory;
this.typeConverter = this.beanFactory.getTypeConverter();
this.environment = environment;
this.placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
this.springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
this.gson = new Gson();
}
-
typeConverter
字段,TypeConverter 对象,详细参见 《Spring 容器中 TypeConverter 对象的使用》 。 -
typeConverterHasConvertIfNecessaryWithFieldParameter
字段,是否使用带 Field 方法参数的TypeConverter#convertIfNecessary(Object, Class, Field)
方法,因为 Spring 3.2.0+ 才有该方法。通过#testTypeConverterHasConvertIfNecessaryWithFieldParameter()
方法,初始化:
private boolean testTypeConverterHasConvertIfNecessaryWithFieldParameter() {
try {
TypeConverter.class.getMethod("convertIfNecessary", Object.class, Class.class, Field.class);
} catch (Throwable ex) {
return false;
}
return true;
}
-
- 通过反射是否存在该方法。
9.2 onChange
1: @Override
2: public void onChange(ConfigChangeEvent changeEvent) {
3: // 获得更新的 KEY 集合
4: Set<String> keys = changeEvent.changedKeys();
5: if (CollectionUtils.isEmpty(keys)) {
6: return;
7: }
8: // 循环 KEY 集合,更新 StringValue
9: for (String key : keys) {
10: // 忽略,若不在 SpringValueRegistry 中
11: // 1. check whether the changed key is relevant
12: Collection<SpringValue> targetValues = springValueRegistry.get(key);
13: if (targetValues == null || targetValues.isEmpty()) {
14: continue;
15: }
16: // 校验是否需要更新
17: // 2. check whether the value is really changed or not (since spring property sources have hierarchies)
18: if (!shouldTriggerAutoUpdate(changeEvent, key)) {
19: continue;
20: }
21: // 循环,更新 SpringValue
22: // 3. update the value
23: for (SpringValue val : targetValues) {
24: updateSpringValue(val);
25: }
26: }
27: }
- 第 3 至 7 行:获得更新的 KEY 集合。
- 第 8 至 26 行:循环 KEY 集合,更新对应的 StringValue 们。
-
第 10 至 15 行:忽略,若不在 SpringValueRegistry 中。因为,不是所有的 Apollo 的配置项,应用程序中都在使用。
-
第 16 至 20 行:调用
#shouldTriggerAutoUpdate(changeEvent, key)
方法,校验是否需要触发更新。代码如下:
-
private boolean shouldTriggerAutoUpdate(ConfigChangeEvent changeEvent, String changedKey) {
ConfigChange configChange = changeEvent.getChange(changedKey);
// 若变更类型为删除,需要触发更新
if (configChange.getChangeType() == PropertyChangeType.DELETED) {
return true;
}
// 若变更类型为新增或修改,判断 environment 的值是否和最新值相等。
// 【高能】!!!
return Objects.equals(environment.getProperty(changedKey), configChange.getNewValue());
}
-
-
- 若变更类型为删除,需要触发更新。
- 若变更类型为新增或删除,比较“神奇”的是判断
environment
对应的值是否和最新值相等。笔者一开始以为会不会是 BUG ?!但是仔细一想,因为客户端,是不考虑 Namespace 的,那么如果存在多个 Namespace 有相同的 KEY,需用通过是否相等,才能判断是不是对应的 Namespace 发生变化。巧妙!!!
-
第 21 至 25 行:循环,调用
#updateSpringValue(SpringValue)
更新 SpringValue 。
-
9.3 updateSpringValue
private void updateSpringValue(SpringValue springValue) {
try {
// 解析值
Object value = resolvePropertyValue(springValue);
// 更新 StringValue
springValue.update(value);
logger.info("Auto update apollo changed value successfully, new value: {}, {}", value, springValue);
} catch (Throwable ex) {
logger.error("Auto update apollo changed value failed, {}", springValue.toString(), ex);
}
}
9.4 resolvePropertyValue
1: /**
2: * Logic transplanted from DefaultListableBeanFactory
3: *
4: * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set, org.springframework.beans.TypeConverter)
5: */
6: private Object resolvePropertyValue(SpringValue springValue) {
7: // value will never be null, as @Value and @ApolloJsonValue will not allow that
8: Object value = placeholderHelper.resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder());
9: // 如果值数据结构是 JSON 类型,则使用 Gson 解析成对应值的类型
10: if (springValue.isJson()) {
11: value = parseJsonValue((String) value, springValue.getGenericType());
12: } else {
13: // 如果类型为 Field
14: if (springValue.isField()) {
15: // org.springframework.beans.TypeConverter#convertIfNecessary(java.lang.Object, java.lang.Class, java.lang.reflect.Field) is available from Spring 3.2.0+
16: if (typeConverterHasConvertIfNecessaryWithFieldParameter) {
17: value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField());
18: } else {
19: value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType());
20: }
21: // 如果类型为 Method
22: } else {
23: value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getMethodParameter());
24: }
25: }
26:
27: return value;
28: }
- 第 8 行:调用
PlaceholderHelper#resolvePropertyValue(beanFactory, beanName, placeholder
方法,解析属性值。代码如下:
/**
* Resolve placeholder property values, e.g.
*
* "${somePropertyValue}" -> "the actual property value"
*/
public Object resolvePropertyValue(ConfigurableBeanFactory beanFactory, String beanName, String placeholder) {
// resolve string value
String strVal = beanFactory.resolveEmbeddedValue(placeholder);
// 获得 BeanDefinition 对象
BeanDefinition bd = (beanFactory.containsBean(beanName) ? beanFactory.getMergedBeanDefinition(beanName) : null);
// resolve expressions like "#{systemProperties.myProp}"
return evaluateBeanDefinitionString(beanFactory, strVal, bd);
}
private Object evaluateBeanDefinitionString(ConfigurableBeanFactory beanFactory, String value, BeanDefinition beanDefinition) {
if (beanFactory.getBeanExpressionResolver() == null) {
return value;
}
Scope scope = (beanDefinition != null ? beanFactory.getRegisteredScope(beanDefinition.getScope()) : null);
return beanFactory.getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(beanFactory, scope));
}
注意,value
是 Object 类型,不一定符合更新 StringValue 的值类型,因此,需要经过转换。
-
第 9 至 11 行:如果值的数据类型是 JSON 类型,则调用
#parseJsonValue(value, targetType)
方法,解析成对应值的类型( Type ) 。代码如下:
private Object parseJsonValue(String json, Type targetType) {
try {
return gson.fromJson(json, targetType);
} catch (Throwable ex) {
logger.error("Parsing json '{}' to type {} failed!", json, targetType, ex);
throw ex;
}
}
-
-
- 这块需要结合
@ApolloJsonValue
注解一起理解,所以见 《Apollo 源码解析 —— 客户端配置 Spring 集成(一)之注解配置》
- 这块需要结合
-
第 12 至 25 行:如果值的数据类型是非 JSON 类型,调用 TypeConverter 的
#convertIfNecessary(...)
方法,转换成对应类型的值。
-
10. SpringInjector
com.ctrip.framework.apollo.spring.util.SpringInjector
,Spring 注入器,实现依赖注入( DI,全称“Dependency Injection” ) 。
类似 DefaultInjector 。但是不要被类名“欺骗”啦,只是注入集成 Spring 需要做的 DI ,例如 SpringValueRegistry 的单例。
10.1 构造方法
/**
* 注入器
*/
private static volatile Injector s_injector;
/**
* 锁
*/
private static final Object lock = new Object();
private static Injector getInjector() {
// 若 Injector 不存在,则进行获得
if (s_injector == null) {
synchronized (lock) {
// 若 Injector 不存在,则进行获得
if (s_injector == null) {
try {
s_injector = Guice.createInjector(new SpringModule());
} catch (Throwable ex) {
ApolloConfigException exception = new ApolloConfigException("Unable to initialize Apollo Spring Injector!", ex);
Tracer.logError(exception);
throw exception;
}
}
}
}
return s_injector;
}
- 使用 SpringModule 类,告诉 Guice 需要 DI 的配置。代码如下:
private static class SpringModule extends AbstractModule {
@Override
protected void configure() {
bind(PlaceholderHelper.class).in(Singleton.class);
bind(ConfigPropertySourceFactory.class).in(Singleton.class);
bind(SpringValueRegistry.class).in(Singleton.class);
}
}
-
- 配置了 PlaceholderHelper、ConfigPropertySourceFactory、SpringValueRegistry 三个单例。
10.2 获得实例
public static <T> T getInstance(Class<T> clazz) {
try {
return getInjector().getInstance(clazz);
} catch (Throwable ex) {
Tracer.logError(ex);
throw new ApolloConfigException(String.format("Unable to load instance for %s!", clazz.getName()), ex);
}
}