文章目录
- 前言
- 一、SpringBoot集成apollo源码分析
- 1. spring.factories
- 1.1 ApolloAutoConfiguration解析
- 1.2ApolloApplicationContextInitializer解析
- 1.2.1 ApolloApplicationContextInitializer#postProcessEnvironment
- 1.2.2 ApolloApplicationContextInitializer#initialize
- 1.2.2.1 configPropertySourceFactory#getConfigPropertySource
- 1.2.2.2 ConfigService#getConfig
- 1.2.2.2 DefaultConfigManager#getConfig
- 1.2.2.3 DefaultConfigFactory#create
- 1.2.2.4 DefaultConfigFactory#createLocalConfigRepository
- 1.2.2.5 LocalFileConfigRepository构造方法
- 1.2.2.6 LocalFileConfigRepository#setUpstreamRepository
- 1.2.2.7 LocalFileConfigRepository#trySync
- 2. apollo配置属性注入
前言
这里就不说
SpringBoot
自动装备的原理,Apollo
与SpringBoot
的整合就是借助了SpringBoot的SPI
机制,下面我们来看下处理流程
一、SpringBoot集成apollo源码分析
1. spring.factories
我们都知道
spring.factories
是SpringBoot
的扩展点,是实现自动装配的核心原理,Apollo
客户端就是借助这个SPI
机制与SpringBoot
整合,我们看下apollo
的jar
包中的META-INFO/spring.factories
文件
1.1 ApolloAutoConfiguration解析
这里看名字是实现
apollo
的自动配置,在配置类中会判断如果spring
中没有PropertySourcesProcessor
这个bean
,那么他会new
一个ConfigPropertySourcesProcessor
到容器中,确保这个bean
一定存在,这个bean的作用我们后续会解析
1.2ApolloApplicationContextInitializer解析
ApolloApplicationContextInitializer
实现了两个接口ApplicationContextInitializer
和EnvironmentPostProcessor
,Spring Boot
启动过程,会先调用EnvironmentPostProcessor.postProcessEnvironment
方法,再调用ApplicationContextInitializer.initialize
方法。也就是Spring Boot
优先准备环境,再初始化容器。
1.2.1 ApolloApplicationContextInitializer#postProcessEnvironment
这里首先就是检查
apollo.bootstrap.eagerLoad.enabled
属性,判断是否允许在Spring
启动初期加载apollo
配置,如果允许则继续检查apollo.bootstrap.enabled
属性判断是否允许加载apollo
,如果都允许,则调用initialize
方法进行初始化
@Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
// should always initialize system properties like app.id in the first place
initializeSystemProperty(configurableEnvironment);
Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);
//EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
if (!eagerLoadEnabled) {
return;
}
Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
if (bootstrapEnabled) {
DeferredLogger.enable();
//初始化Apollo的配置
initialize(configurableEnvironment);
}
}
1.2.2 ApolloApplicationContextInitializer#initialize
- 这里会发现他从
apollo.bootstrap.namespaces
属性配置中获取namespaces
,然后遍历命名空间列表,调用ConfigService$getConfig
拉取远程配置,然后将拉取到的配置包装成PropertySource
,然后将包装好的PropertySource
添加到CompositePropertySource
内部,最后CompositePropertySource
属性源包装类添加到Spring 的 Environment
环境中,注意是插入在属性源列表的头部,因为取属性的时候其实是遍历这个属性源列表来查找,找到即返回,所以出现同名属性是以前面的为准
protected void initialize(ConfigurableEnvironment environment) {
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//already initialized, replay the logs that were printed before the logging system was initialized
DeferredLogger.replayTo();
return;
}
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
logger.debug("Apollo bootstrap namespaces: {}", namespaces);
List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);
CompositePropertySource composite;
final ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
if (configUtil.isPropertyNamesCacheEnabled()) {
composite = new CachedCompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
} else {
composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
}
for (String namespace : namespaceList) {
//获取配置
Config config = ConfigService.getConfig(namespace);
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
environment.getPropertySources().addFirst(composite);
}
1.2.2.1 configPropertySourceFactory#getConfigPropertySource
- 将
Apollo
的Config
封装成Spring Config
的ConfigPropertySource
对象- 将新生成的
ConfigPropertySource
对象添加到内部列表,方便后续为每个配置实例添加配置变化监听器。
public ConfigPropertySource getConfigPropertySource(String name, Config source) {
//将Apollo的Config配置封装为继承自Spring内置的EnumerablePropertySource类的ConfigPropertySource对象
ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source);
//将新生成的ConfigPropertySource对象缓存到内部列表,以备后续为每个配置实例添加配置变化监听器使用。
configPropertySources.add(configPropertySource);
return configPropertySource;
}
1.2.2.2 ConfigService#getConfig
- 这里
getManager
去获取ConfigManager
实例,采用了java
的ServiceLoader
机制,是SPI
机制- 这里看
ConfigManager
的实现类其实只有一个,就是DefaultConfigManager
public static Config getConfig(String namespace) {
return s_instance.getManager().getConfig(namespace);
}
private ConfigManager getManager() {
//双重检查锁防止重复创建
if (m_configManager == null) {
synchronized (this) {
if (m_configManager == null) {
//使用ApolloInjector获取ConfigManager实例,这里是采用了java的ServiceLoeader机制
m_configManager = ApolloInjector.getInstance(ConfigManager.class);
}
}
}
return m_configManager;
}
1.2.2.2 DefaultConfigManager#getConfig
- 这里首先从缓存中获取配置,缓存中没有则从远程拉取
- 这里使用
SPI
机制获取Factory
,默认是DefaultConfigFactory
,这里会调用DefaultConfigFactory#create
从远程拉取配置
public Config getConfig(String namespace) {
Config config = m_configs.get(namespace);
if (config == null) {
synchronized (this) {
config = m_configs.get(namespace);
if (config == null) {
//这里Factory创建也使用了ServiceLoader机制,默认是DefaultConfigFactory
ConfigFactory factory = m_factoryManager.getFactory(namespace);
config = factory.create(namespace);
m_configs.put(namespace, config);
}
}
}
return config;
}
1.2.2.3 DefaultConfigFactory#create
1.这里重点是首先调用
createLocalConfigRepository(namespace)
会创建LocalConfigRepository
,在createLocalConfigRepository()
方法中又会调用createRemoteConfigRepository(namespace)
创建RemoteConfigRepository
,最后又会调用createRepositoryConfig
创建DefaultConfig
对象,内部持有LocalConfigRepository
2. 对象创建时序:DefaultConfig -> LocalFileConfigRepository -> RemoteConfigRepository
3. 对象关系:DefaultConfig -> 持有 LocalFileConfigRepository, LocalFileConfigRepository -> 持有 RemoteConfigRepository
3. 配置变化传播时序:RemoteConfigRepository -> LocalFileConfigRepository -> DefaultConfig -> ConfigChangeListener
public Config create(String namespace) {
//确定本地配置缓存文件的格式
ConfigFileFormat format = determineFileFormat(namespace);
if (ConfigFileFormat.isPropertiesCompatible(format)) {
return this.createRepositoryConfig(namespace, createPropertiesCompatibleFileConfigRepository(namespace, format));
}
//这里会创建DefaultConfig,内部会持有->LocalFileConfigRepository,LocalFileConfigRepository内部会有RemoteConfigRepository对象
return this.createRepositoryConfig(namespace, createLocalConfigRepository(namespace));
}
1.2.2.4 DefaultConfigFactory#createLocalConfigRepository
LocalFileConfigRepository
内部持有RemoteConfigRepository
,这里RemoteConfigRepository
之前我们已经解析过,构造方法中包含定时拉取和长轮询逻辑,这里初始化RemoteConfigRepository会去远程将配置拉取到本地缓存起来
LocalFileConfigRepository createLocalConfigRepository(String namespace) {
if (m_configUtil.isInLocalMode()) {
logger.warn(
"==== Apollo is in local mode! Won't pull configs from remote server for namespace {} ! ====",
namespace);
return new LocalFileConfigRepository(namespace);
}
//这里内部持有RemoteConfigRepository对象
return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
}
1.2.2.5 LocalFileConfigRepository构造方法
- 这里会查找当前
apiiId
本地配置缓存文件,没有则创建一个- 将创建完毕的
RemoteConfigRepository
设置为上游配置仓库- 调用
trySync
方法尝试同步
public LocalFileConfigRepository(String namespace, ConfigRepository upstream) {
m_namespace = namespace;
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
//查找当前appid本地配置缓存文件,没有则创建一个,然后设置到属性中
this.setLocalCacheDir(findLocalCacheDir(), false);
//这里将创建完成的RemoteConfigRepository对象设置为上游配置仓库
this.setUpstreamRepository(upstream);
//尝试同步配置
this.trySync();
}
1.2.2.6 LocalFileConfigRepository#setUpstreamRepository
- 这里重点会将自己注册到
upstreamConfigRepository
的changeListeners
中,这样RemoteConfigRepository
中配置发生改变,就会感知到。
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
if (upstreamConfigRepository == null) {
return;
}
//clear previous listener
if (m_upstream != null) {
m_upstream.removeChangeListener(this);
}
m_upstream = upstreamConfigRepository;
//这里将本地仓库作为监听者添加到上游仓库的监听者列表中如果上游仓库有变化,就会通知对应的监听者
upstreamConfigRepository.addChangeListener(this);
}
1.2.2.7 LocalFileConfigRepository#trySync
最终会调用到sync方法,这个方法就是使用上游仓库的配置更新本地文件,将配置缓存到磁盘上。
protected void sync() {
//sync with upstream immediately
//使用上游仓库的配置更新本地文件
boolean syncFromUpstreamResultSuccess = trySyncFromUpstream();
if (syncFromUpstreamResultSuccess) {
return;
}
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncLocalConfig");
Throwable exception = null;
try {
transaction.addData("Basedir", m_baseDir.getAbsolutePath());
//最终将配置持久化成文件
m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
m_sourceType = ConfigSourceType.LOCAL;
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
exception = ex;
//ignore
} finally {
transaction.complete();
}
if (m_fileProperties == null) {
m_sourceType = ConfigSourceType.NONE;
throw new ApolloConfigException(
"Load config from local config failed!", exception);
}
}
2. apollo配置属性注入
2.1 apollo组件注入@EnableApolloConfig
在
SpringBoot
中使用Apollo
客户端会在启动类上标注@EnableApolloConfig
注解,这里可以看到使用Import导入了一个ApolloConfigRegistrar
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
/**
* Apollo namespaces to inject configuration into Spring Property Sources.
*/
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
/**
* The order of the apollo config, default is {@link Ordered#LOWEST_PRECEDENCE}, which is Integer.MAX_VALUE.
* If there are properties with the same name in different apollo configs, the apollo config with smaller order wins.
* @return
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
2.2.1 ApolloConfigRegistrar
- setEnvironment方法将Environment环境保存到helper中
- registerBeanDefinitions方法调用helper#registerBeanDefinitions
- help的实现是DefaultApolloConfigRegistrarHelper
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private final ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);
/**
* 回调方法,会在Spring解析完BeanDefinition将其加载进容器的准备阶段回调,这个方法也相当于一个入口,真正完成Apollo组件注册的逻辑在
* DefaultApolloConfigRegistrarHelper#registerBeanDefinitions() 方法中
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
helper.registerBeanDefinitions(importingClassMetadata, registry);
}
@Override
public void setEnvironment(Environment environment) {
//将Environment环境保存到ApolloConfigRegistrarHelper中
this.helper.setEnvironment(environment);
}
2.2.2 DefaultApolloConfigRegistrarHelper#registerBeanDefinitions
- 首先解析
@EnableApolloConfig
注解,解析注解中的属性- 调用工具类将
Apollo
相关组件类注册到spring
容器中,关键组件有PropertySourcesProcesso
r(用于拉取@EnableApolloConfig
配置的namespace
的远程配置),ApolloAnnotationProcessor
(用于处理Apollo
的专用注解),SpringValueProcessor
(用于处理@Value
注解标注的类成员变量和方法),SpringValueDefinitionProcessor
(处理XML
文件中的占位符),PropertySourcesPlaceholderConfigurer
(占位符处理,主要就是将@Vlaue(${ss.ss}))
中解析出关键字,然后再使用key
从PropertySourcesPropertyResolver
从PropertySource
中找到对应的属性值替换掉占位符
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//解析@EnableApolloConfig注解,将注解中配置的字符串数组处理为namespace数组,并调用PropertySourceProcessor.addNamespaces()方法将其缓存起来
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));
final String[] namespaces = attributes.getStringArray("value");
final int order = attributes.getNumber("order");
final String[] resolvedNamespaces = this.resolveNamespaces(namespaces);
PropertySourcesProcessor.addNamespaces(Lists.newArrayList(resolvedNamespaces), order);
Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
// to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
propertySourcesPlaceholderPropertyValues.put("order", 0);
//调用工具类尝试将Apollo组件类包装为BeanDefinition并注册到容器中,可以看到注册的Apollo关键组件有PropertySourcesProcessor(用于拉取
// @EnableApolloConfig配置的 namespace 的远程配置),ApolloAnnotationProcessor(用于处理 Apollo的专用注解),SpringValueProcessor(用于
// 处理@Value 注解标注的类成员变量和对象方法),SpringValueDefinitionProcessor(用于处理xml文件中的占位符),注意组件对象的实例化有优先级顺序,getOrder()返回
//的值越小,优先级越高,对象创建的实际也就越靠前
//PropertySourcesPlaceholderConfigurer是 SpringBoot 框架自身的占位符处理配置,占位符的处理主要是将 ${apollo.value} 这样的字符串解析出 关键字 apollo.value,
// 再使用这个 key 通过 PropertySourcesPropertyResolver 从 PropertySource 中找到对应的属性值替换掉占位符
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),
PropertySourcesProcessor.class);
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
ApolloAnnotationProcessor.class);
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(),
SpringValueProcessor.class);
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(),
SpringValueDefinitionProcessor.class);
}
2.2.3 PropertySourcesProcessor#postProcessBeanFactory
- 拉取远程 namespace配置并初始化PropertySources
- 给所有缓存在本地的Config配置添加监听器
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.configUtil = ApolloInjector.getInstance(ConfigUtil.class);
//拉取远程namespace的配置
initializePropertySources();
//给所有缓存在本地的Config配置添加监听器
initializeAutoUpdatePropertiesFeature(beanFactory);
}
2.2.3.1 PropertySourcesProcessor#initializePropertySources
这个上面已经分析过了,首先就是拉取配置,然后将拉取到的配置包装成
PropertySource
,然后添加到CompositePropertySource
内部,然后将CompositePropertySource
添加到spring
的Envvironment
中
private void initializePropertySources() {
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME)) {
//already initialized
return;
}
CompositePropertySource composite;
if (configUtil.isPropertyNamesCacheEnabled()) {
composite = new CachedCompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
} else {
composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
}
//sort by order asc
ImmutableSortedSet<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
Iterator<Integer> iterator = orders.iterator();
while (iterator.hasNext()) {
int order = iterator.next();
for (String namespace : NAMESPACE_NAMES.get(order)) {
//依次拉取每个命名空间的远程配置
Config config = ConfigService.getConfig(namespace);
//调用 ConfigPropertySourceFactory#getConfigPropertySource() 缓存从远端拉取的配置,并将其包装为 PropertySource
// ,最终将所有拉取到的远端配置聚合到一个以 ApolloPropertySources 为 key 的属性源包装类 CompositePropertySource 的内部
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
}
// clean up
NAMESPACE_NAMES.clear();
// add after the bootstrap property source or to the first
if (environment.getPropertySources()
.contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
// ensure ApolloBootstrapPropertySources is still the first
ensureBootstrapPropertyPrecedence(environment);
environment.getPropertySources()
.addAfter(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME, composite);
} else {
//将 CompositePropertySource 属性源包装类添加到 Spring 的 Environment 环境中,此处要保证 Bootstrap 的 namespace 配置如存在的话必须在属性源列表头部
environment.getPropertySources().addFirst(composite);
}
}
2.2.3.2 PropertySourcesProcessor#initializeAutoUpdatePropertiesFeature
这里会给拉取到的所有配置添加自动更新的监听器
private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() ||
!AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {
return;
}
AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(
environment, beanFactory);
List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
//给所有的apollo的配置对象添加自动更新的监听器
for (ConfigPropertySource configPropertySource : configPropertySources) {
configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
}
}
2.2.4 SpringValueProcessor#postProcessBeforeInitialization
这里就是将@Vlaue标注的bean字段缓存下来,然后通过反射使用配置替换原本的值,然后后续如果对应的Apollo配置有变化,也可以通过监听器感知到然后通过反射更新
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
super.postProcessBeforeInitialization(bean, beanName);
processBeanPropertyValues(bean, beanName);
}
return bean;
}
2.2.4.1 super#postProcessBeforeInitialization
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//这个就是将被@Value标注的Bean字段缓存下来,如果后续这个字段对应的Apollo配置有更新,
//可以通过反射将更新后的值更新为字段值
Class clazz = bean.getClass();
//遍历所有的属性
for (Field field : findAllField(clazz)) {
processField(bean, beanName, field);
}
//遍历所有的方法
for (Method method : findAllMethod(clazz)) {
processMethod(bean, beanName, method);
}
return bean;
}
2.2.4.2 SpringValueProcessor#processField
这里处理属性和方法逻辑一致,就是判断有没有注解,存在就解析出来注解中的
key
,然后将关系封装成SpringValue
并缓存起来
protected void processField(Object bean, String beanName, Field field) {
// register @Value on field
//判断属性上面有没有@Value注解
Value value = field.getAnnotation(Value.class);
if (value == null) {
return;
}
//如果注解存在,将注解占位符字符串中的属性Key解析出来,
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
if (keys.isEmpty()) {
return;
}
//对于每一个key,将其和Field字段等关键信息封装为SpringValue对象,并通过springValueRegistry#register缓存起来
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
springValueRegistry.register(beanFactory, key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
2.2.4.3 SpringValueProcessor#processBeanPropertyValues
这里和上面逻辑类似,就是检查有没有手动设置SpringValueDefinition,如果有,则将其转成SpringValue并缓存起来
private void processBeanPropertyValues(Object bean, String beanName) {
Collection<SpringValueDefinition> propertySpringValues = beanName2SpringValueDefinitions
.get(beanName);
if (propertySpringValues == null || propertySpringValues.isEmpty()) {
return;
}
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(beanFactory, definition.getKey(), springValue);
logger.debug("Monitoring {}", springValue);
} catch (Throwable ex) {
logger.error("Failed to enable auto update feature for {}.{}", bean.getClass(),
definition.getPropertyName());
}
}
// clear
beanName2SpringValueDefinitions.removeAll(beanName);
}
2.2 apollo配置属性自动变更
在上面解析的时候说过对每一个配置都会设置一个自动配置更新的监听器AutoUpdateConfigChangeListener,这个监听器实际会设置到AbstractConfig中。
之前解析RemoteConfigRepository说过,当定时或者长轮询服务收到配置变化的时候,会调用fireRepositoryChange将触发监听器,将配置变化通知出去。
2.2.1 RemoteConfigRepository#fireRepositoryChange
这里触发监听器,对应的LocalFileConfigRepository之前已经注册了,所以会感知到。
protected void fireRepositoryChange(String namespace, Properties newProperties) {
for (RepositoryChangeListener listener : m_listeners) {
try {
// 触发监听器
listener.onRepositoryChange(namespace, newProperties);
} catch (Throwable ex) {
Tracer.logError(ex);
logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex);
}
}
}
2.2.2 LocalFileConfigRepository#onRepositoryChange
这里首先会根据配置的更新修改本地文件,然后再将事件传播出去,这里DefaultConfig会感知到
public void onRepositoryChange(String namespace, Properties newProperties) {
if (newProperties.equals(m_fileProperties)) {
return;
}
Properties newFileProperties = propertiesFactory.getPropertiesInstance();
newFileProperties.putAll(newProperties);
updateFileProperties(newFileProperties, m_upstream.getSourceType());
this.fireRepositoryChange(namespace, newProperties);
}
2.2.3 DefaultConfig#onRepositoryChange
这里解析Properties,然后计算出实际变更的属性值,然后将变更后的值传播出去。
public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
if (newProperties.equals(m_configProperties.get())) {
return;
}
ConfigSourceType sourceType = m_configRepository.getSourceType();
Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
newConfigProperties.putAll(newProperties);
//更新配置属性,并计算出实际变更的属性
Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties,
sourceType);
//check double checked result
if (actualChanges.isEmpty()) {
return;
}
//将配置变化传播出去
this.fireConfigChange(m_namespace, actualChanges);
Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
}
2.2.4 DefaultConfig#fireConfigChange
这里首先找到监听这个配置的监听器,然后异步通知对应的监听器,因为之前我们给配置注册了一个
AutoUpdateConfigChangeListener
,所以会调用到AutoUpdateConfigChangeListener#onChange
方法
protected void fireConfigChange(String namespace, Map<String, ConfigChange> changes) {
final Set<String> changedKeys = changes.keySet();
final List<ConfigChangeListener> listeners = this.findMatchedConfigChangeListeners(changedKeys);
// notify those listeners
for (ConfigChangeListener listener : listeners) {
Set<String> interestedChangedKeys = resolveInterestedChangedKeys(listener, changedKeys);
InterestedConfigChangeEvent interestedConfigChangeEvent = new InterestedConfigChangeEvent(
namespace, changes, interestedChangedKeys);
this.notifyAsync(listener, interestedConfigChangeEvent);
}
}
2.2.5 AutoUpdateConfigChangeListener#onChange
从配置变化事件
ConfigChangeEvent
取出发生了变化的key
,再检查是否属于当前BeanFactory
,这里用到了之前的注册器springValueRegistry
,
检查通过则调用AutoUpdateConfigChangeListener#updateSpringValue()
更新@Value
标注的字段/方法值
public void onChange(ConfigChangeEvent changeEvent) {
Set<String> keys = changeEvent.changedKeys();
if (CollectionUtils.isEmpty(keys)) {
return;
}
for (String key : keys) {
// 1. check whether the changed key is relevant
Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
if (targetValues == null || targetValues.isEmpty()) {
continue;
}
// 2. update the value
for (SpringValue val : targetValues) {
updateSpringValue(val);
}
}
}
2.2.6 AutoUpdateConfigChangeListener#updateSpringValue
首先调用 AutoUpdateConfigChangeListener#resolvePropertyValue() 方法借助 SpringBoot 的组件将 @Value 中配置的占位符替换为 PropertySource 中的对应 key 的属性值。
调用 SpringValue#update()方法实际完成属性值的更新,这里就是直接通过反射更改bean属性。
private void updateSpringValue(SpringValue springValue) {
try {
//借助springboot的组件将@value中配置的占位符替换为PropertySource中的对应key的属性值,
Object value = resolvePropertyValue(springValue);
//完成属性值的更新
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);
}
}