Config config = ConfigService.getAppConfig();
config.getProperty("key1");
配置META-INF/app.properties文件
# 应用id
app.id=250001
# 灰度发布时会使用
# apollo.label=myLabel
# 初始化时可以读取ConfigService的服务地址
apollo.meta=http://localhost:8080
MetaServerProvider接口用于获取MetaServer地址;默认实现类通过SPI注入为DefaultMetaServerProvider。
/**
* 初始化MetaServer服务器地址 从System.getProperty System.getenv Foundation.server Foundation.app
*/
private String initMetaServerAddress() {
// 1. Get from System Property
String metaAddress = System.getProperty(ConfigConsts.APOLLO_META_KEY);
if (Strings.isNullOrEmpty(metaAddress)) {
// 2. Get from OS environment variable, which could not contain dot and is normally in UPPER case
metaAddress = System.getenv(ApolloClientSystemConsts.APOLLO_META_ENVIRONMENT_VARIABLES);
}
if (Strings.isNullOrEmpty(metaAddress)) {
// 3. Get from server.properties
metaAddress = Foundation.server().getProperty(ConfigConsts.APOLLO_META_KEY, null);
}
if (Strings.isNullOrEmpty(metaAddress)) {
// 4. Get from app.properties 读取META/app.properties
metaAddress = Foundation.app().getProperty(ConfigConsts.APOLLO_META_KEY, null);
}
if (Strings.isNullOrEmpty(metaAddress)) {
logger.warn(
"Could not find meta server address, because it is not available in neither (1) JVM system property 'apollo.meta', (2) OS env variable 'APOLLO_META' (3) property 'apollo.meta' from server.properties nor (4) property 'apollo.meta' from app.properties");
} else {
metaAddress = metaAddress.trim();
logger.info("Located meta services from apollo.meta configuration: {}!", metaAddress);
}
return metaAddress;
}
初始化:内部采用SPI机制和Google的guice依赖注入框架。
private static class ApolloModule extends AbstractModule {
@Override
protected void configure() {
bind(ConfigManager.class).to(DefaultConfigManager.class).in(Singleton.class);
bind(ConfigFactoryManager.class).to(DefaultConfigFactoryManager.class).in(Singleton.class);
bind(ConfigRegistry.class).to(DefaultConfigRegistry.class).in(Singleton.class);
bind(ConfigFactory.class).to(DefaultConfigFactory.class).in(Singleton.class);
bind(ConfigUtil.class).in(Singleton.class);
bind(HttpClient.class).to(DefaultHttpClient.class).in(Singleton.class);
bind(ConfigServiceLocator.class).in(Singleton.class);
bind(RemoteConfigLongPollService.class).in(Singleton.class);
bind(YamlParser.class).in(Singleton.class);
bind(PropertiesFactory.class).to(DefaultPropertiesFactory.class).in(Singleton.class);
}
}
ConfigService.getAppConfig的实现
//ConfigService.java
public static Config getAppConfig() {
return getConfig(ConfigConsts.NAMESPACE_APPLICATION);
}
public static Config getConfig(String namespace) {
return s_instance.getManager().getConfig(namespace);
}
// DefaultConfigManager
@Override
public Config getConfig(String namespace) {
Config config = m_configs.get(namespace);
if (config == null) {
synchronized (this) {
config = m_configs.get(namespace);
if (config == null) {
ConfigFactory factory = m_factoryManager.getFactory(namespace);
// 创建Config
config = factory.create(namespace);
m_configs.put(namespace, config);
}
}
}
return config;
}
// DefaultFactory
@Override
public Config create(String namespace) {
ConfigFileFormat format = determineFileFormat(namespace);
ConfigRepository configRepository = null;
// although ConfigFileFormat.Properties are compatible with themselves we
// should not create a PropertiesCompatibleFileConfigRepository for them
// calling the method `createLocalConfigRepository(...)` is more suitable
// for ConfigFileFormat.Properties
// 如果配置文件是yaml或yml时,创建本地文件为properties格式的文件
if (ConfigFileFormat.isPropertiesCompatible(format) &&
format != ConfigFileFormat.Properties) {
configRepository = createPropertiesCompatibleFileConfigRepository(namespace, format);
} else {
configRepository = createLocalConfigRepository(namespace);
}
logger.debug("Created a configuration repository of type [{}] for namespace [{}]",
configRepository.getClass().getName(), namespace);
return this.createRepositoryConfig(namespace, configRepository);
}
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);
}
// 默认为本地加远程模式
return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
}
ConfigRepository创建过程:先创建一个远程的Repository,然后将其包装到本地,生成本地文件保存。
// DefaultFactory
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);
}
// 默认为本地加远程模式
return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
}
RemoteConfigRepository createRemoteConfigRepository(String namespace) {
return new RemoteConfigRepository(namespace);
}
// RemoteConfigRepository
public RemoteConfigRepository(String namespace) {
m_namespace = namespace;
m_configCache = new AtomicReference<>();
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
m_httpClient = ApolloInjector.getInstance(HttpClient.class);
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
m_longPollServiceDto = new AtomicReference<>();
m_remoteMessages = new AtomicReference<>();
m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
m_configNeedForceRefresh = new AtomicBoolean(true);
m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
m_configUtil.getOnErrorRetryInterval() * 8);
// 同步配置
this.trySync();
// 定时刷新任务 默认为5min
this.schedulePeriodicRefresh();
// 长轮询
this.scheduleLongPollingRefresh();
}
protected boolean trySync() {
try {
sync();
return true;
} catch (Throwable ex) {
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
logger
.warn("Sync config failed, will retry. Repository {}, reason: {}", this.getClass(), ExceptionUtil
.getDetailMessage(ex));
}
return false;
}
/**
同步配置,三个地方进行调用
1、初始化时
2、定时刷新任务
3、长轮询发现配置有更新消息的时候
*/
@Override
protected synchronized void sync() {
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");
try {
ApolloConfig previous = m_configCache.get();
// url: /configs/250001/default/application
ApolloConfig current = loadApolloConfig();
//reference equals means HTTP 304
if (previous != current) {
logger.debug("Remote Config refreshed!");
m_configCache.set(current);
// 回调监听器列表
this.fireRepositoryChange(m_namespace, this.getConfig());
}
if (current != null) {
Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
current.getReleaseKey());
}
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
transaction.setStatus(ex);
throw ex;
} finally {
transaction.complete();
}
}
同时有两种策略:
// RemoteConfigRepository
public RemoteConfigRepository(String namespace) {
m_namespace = namespace;
m_configCache = new AtomicReference<>();
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
m_httpClient = ApolloInjector.getInstance(HttpClient.class);
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
m_longPollServiceDto = new AtomicReference<>();
m_remoteMessages = new AtomicReference<>();
m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
m_configNeedForceRefresh = new AtomicBoolean(true);
m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
m_configUtil.getOnErrorRetryInterval() * 8);
// 同步配置
this.trySync();
// 定时刷新任务 默认为5min
this.schedulePeriodicRefresh();
// 长轮询
this.scheduleLongPollingRefresh();
}
private void schedulePeriodicRefresh() {
logger.debug("Schedule periodic refresh with interval: {} {}",
m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
// 默认为5min拉去一次配置
m_executorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
logger.debug("refresh config for namespace: {}", m_namespace);
trySync();
Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
}
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
m_configUtil.getRefreshIntervalTimeUnit());
}
private void scheduleLongPollingRefresh() {
remoteConfigLongPollService.submit(m_namespace, this);
}
长轮询模拟推送
// RemoteConfigLongPollService
public boolean submit(String namespace, RemoteConfigRepository remoteConfigRepository) {
boolean added = m_longPollNamespaces.put(namespace, remoteConfigRepository);
m_notifications.putIfAbsent(namespace, INIT_NOTIFICATION_ID);
if (!m_longPollStarted.get()) {
// 开始长轮询
startLongPolling();
}
return added;
}
private void startLongPolling() {
if (!m_longPollStarted.compareAndSet(false, true)) {
//already started
return;
}
try {
final String appId = m_configUtil.getAppId();
final String cluster = m_configUtil.getCluster();
final String dataCenter = m_configUtil.getDataCenter();
final String secret = m_configUtil.getAccessKeySecret();
final long longPollingInitialDelayInMills = m_configUtil.getLongPollingInitialDelayInMills();
m_longPollingService.submit(new Runnable() {
@Override
public void run() {
if (longPollingInitialDelayInMills > 0) {
try {
logger.debug("Long polling will start in {} ms.", longPollingInitialDelayInMills);
TimeUnit.MILLISECONDS.sleep(longPollingInitialDelayInMills);
} catch (InterruptedException e) {
//ignore
}
}
doLongPollingRefresh(appId, cluster, dataCenter, secret);
}
});
} catch (Throwable ex) {
m_longPollStarted.set(false);
ApolloConfigException exception =
new ApolloConfigException("Schedule long polling refresh failed", ex);
Tracer.logError(exception);
logger.warn(ExceptionUtil.getDetailMessage(exception));
}
}
/**
url:
*/
private void doLongPollingRefresh(String appId, String cluster, String dataCenter, String secret) {
final Random random = new Random();
ServiceDTO lastServiceDto = null;
// 进行长轮询
while (!m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) {
// 限流
if (!m_longPollRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {
//wait at most 5 seconds
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
}
}
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "pollNotification");
String url = null;
try {
if (lastServiceDto == null) {
// 从远程服务器端查询ConfigService服务地址
List<ServiceDTO> configServices = getConfigServices();
lastServiceDto = configServices.get(random.nextInt(configServices.size()));
}
url =
assembleLongPollRefreshUrl(lastServiceDto.getHomepageUrl(), appId, cluster, dataCenter,
m_notifications);
logger.debug("Long polling from {}", url);
// url: /notifications/v2
HttpRequest request = new HttpRequest(url);
request.setReadTimeout(LONG_POLLING_READ_TIMEOUT);
if (!StringUtils.isBlank(secret)) {
Map<String, String> headers = Signature.buildHttpHeaders(url, appId, secret);
request.setHeaders(headers);
}
transaction.addData("Url", url);
final HttpResponse<List<ApolloConfigNotification>> response =
m_httpClient.doGet(request, m_responseType);
// 如果response响应值为200表示有新的更新消息
logger.debug("Long polling response: {}, url: {}", response.getStatusCode(), url);
if (response.getStatusCode() == 200 && response.getBody() != null) {
updateNotifications(response.getBody());
updateRemoteNotifications(response.getBody());
transaction.addData("Result", response.getBody().toString());
// 根据响应通知 RemoteConfigRepository 同步配置
notify(lastServiceDto, response.getBody());
}
//try to load balance
if (response.getStatusCode() == 304 && random.nextBoolean()) {
lastServiceDto = null;
}
m_longPollFailSchedulePolicyInSecond.success();
transaction.addData("StatusCode", response.getStatusCode());
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
lastServiceDto = null;
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
long sleepTimeInSecond = m_longPollFailSchedulePolicyInSecond.fail();
logger.warn(
"Long polling failed, will retry in {} seconds. appId: {}, cluster: {}, namespaces: {}, long polling url: {}, reason: {}",
sleepTimeInSecond, appId, cluster, assembleNamespaces(), url, ExceptionUtil.getDetailMessage(ex));
try {
TimeUnit.SECONDS.sleep(sleepTimeInSecond);
} catch (InterruptedException ie) {
//ignore
}
} finally {
transaction.complete();
}
}
}
DefaultConfig实现了RepositoryChangeListener。
LocalFileConfigRepository也实现了RepositoryChangeListener。
// RemoteConfigRepository
/**
同步配置,三个地方进行调用
1、初始化时
2、定时刷新任务
3、长轮询发现配置有更新消息的时候
*/
@Override
protected synchronized void sync() {
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");
try {
ApolloConfig previous = m_configCache.get();
// url: /configs/250001/default/application
ApolloConfig current = loadApolloConfig();
//reference equals means HTTP 304
if (previous != current) {
logger.debug("Remote Config refreshed!");
m_configCache.set(current);
// 回调监听器列表 通知DefaultConfig LocalFileConfigRepository
this.fireRepositoryChange(m_namespace, this.getConfig());
}
if (current != null) {
Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
current.getReleaseKey());
}
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
transaction.setStatus(ex);
throw ex;
} finally {
transaction.complete();
}
}
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);
}
}
}
DefaultConfig类实现了RepositoryChangeListener接口,在RemoteConfigRepository发生改变的时候会进行回调。
同时Config类也有监听器设置,可以监听Config的属性变化。
// DefaultConfig
public DefaultConfig(String namespace, ConfigRepository configRepository) {
m_namespace = namespace;
// 从META-INF/config/xxx.properties中加载配置
m_resourceProperties = loadFromResource(m_namespace);
m_configRepository = configRepository;
m_configProperties = new AtomicReference<>();
m_warnLogRateLimiter = RateLimiter.create(0.017); // 1 warning log output per minute
initialize();
}
private void initialize() {
try {
// 从ConfigRepository中获取配置
updateConfig(m_configRepository.getConfig(), m_configRepository.getSourceType());
} catch (Throwable ex) {
Tracer.logError(ex);
logger.warn("Init Apollo Local Config failed - namespace: {}, reason: {}.",
m_namespace, ExceptionUtil.getDetailMessage(ex));
} finally {
//register the change listener no matter config repository is working or not
//so that whenever config repository is recovered, config could get changed
// 设置ConfigRepository的监听器
m_configRepository.addChangeListener(this);
}
}
@Override
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;
}
// Config的监听器回调
this.fireConfigChange(m_namespace, actualChanges);
Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
}
LocalFileConfigRepository创建时传入ConfigRepository,也会设置自身为其监听器,及时更新本地文件。
// LocalConfigFileRepository
public LocalFileConfigRepository(String namespace, ConfigRepository upstream) {
m_namespace = namespace;
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
this.setLocalCacheDir(findLocalCacheDir(), false);
// 设置自身为upstream的监听器
this.setUpstreamRepository(upstream);
this.trySync();
}
@Override
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);
}
/**
* 监听回调方法
*/
@Override
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);
}
1、远程服务端通过DefferedResult完成异步阻塞。
2、通过ReleaseMessage表实现一个简单的消息队列,对消息进行推送。
// ReleaseMessageScanner
@Override
public void afterPropertiesSet() throws Exception {
databaseScanInterval = bizConfig.releaseMessageScanIntervalInMilli();
maxIdScanned = loadLargestMessageId();
// 定时任务 1s 查询数据库ReleaseMessage表,发送Message
executorService.scheduleWithFixedDelay(() -> {
Transaction transaction = Tracer.newTransaction("Apollo.ReleaseMessageScanner", "scanMessage");
try {
scanMissingMessages();
scanMessages();
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
transaction.setStatus(ex);
logger.error("Scan and send message failed", ex);
} finally {
transaction.complete();
}
}, databaseScanInterval, databaseScanInterval, TimeUnit.MILLISECONDS);
}
private void scanMessages() {
boolean hasMoreMessages = true;
while (hasMoreMessages && !Thread.currentThread().isInterrupted()) {
hasMoreMessages = scanAndSendMessages();
}
}
private boolean scanAndSendMessages() {
//current batch is 500
List<ReleaseMessage> releaseMessages =
releaseMessageRepository.findFirst500ByIdGreaterThanOrderByIdAsc(maxIdScanned);
if (CollectionUtils.isEmpty(releaseMessages)) {
return false;
}
// 监听器处理消息 此处发送ReleaseMessage给相关监听器
fireMessageScanned(releaseMessages);
int messageScanned = releaseMessages.size();
long newMaxIdScanned = releaseMessages.get(messageScanned - 1).getId();
// check id gaps, possible reasons are release message not committed yet or already rolled back
if (newMaxIdScanned - maxIdScanned > messageScanned) {
recordMissingReleaseMessageIds(releaseMessages, maxIdScanned);
}
maxIdScanned = newMaxIdScanned;
return messageScanned == 500;
}
// NotificationControllerV2
/**
* 处理ReleaseMessage,对DeferredResult进行设置,将其从挂起状态唤醒并返回
*/
@Override
public void handleMessage(ReleaseMessage message, String channel) {
logger.info("message received - channel: {}, message: {}", channel, message);
String content = message.getMessage();
Tracer.logEvent("Apollo.LongPoll.Messages", content);
if (!Topics.APOLLO_RELEASE_TOPIC.equals(channel) || Strings.isNullOrEmpty(content)) {
return;
}
String changedNamespace = retrieveNamespaceFromReleaseMessage.apply(content);
if (Strings.isNullOrEmpty(changedNamespace)) {
logger.error("message format invalid - {}", content);
return;
}
if (!deferredResults.containsKey(content)) {
return;
}
//create a new list to avoid ConcurrentModificationException
// 获取ReferredResultWrapper列表
List<DeferredResultWrapper> results = Lists.newArrayList(deferredResults.get(content));
// 设置返回值
ApolloConfigNotification configNotification = new ApolloConfigNotification(changedNamespace, message.getId());
configNotification.addMessage(content, message.getId());
//do async notification if too many clients
// 如果需要处理太多消息则每处理100个停顿100ms
if (results.size() > bizConfig.releaseMessageNotificationBatch()) {
largeNotificationBatchExecutorService.submit(() -> {
logger.debug("Async notify {} clients for key {} with batch {}", results.size(), content,
bizConfig.releaseMessageNotificationBatch());
for (int i = 0; i < results.size(); i++) {
if (i > 0 && i % bizConfig.releaseMessageNotificationBatch() == 0) {
try {
TimeUnit.MILLISECONDS.sleep(bizConfig.releaseMessageNotificationBatchIntervalInMilli());
} catch (InterruptedException e) {
//ignore
}
}
logger.debug("Async notify {}", results.get(i));
results.get(i).setResult(configNotification);
}
});
return;
}
logger.debug("Notify {} clients for key {}", results.size(), content);
for (DeferredResultWrapper result : results) {
result.setResult(configNotification);
}
logger.debug("Notification completed");
}
spring.factories:配置了三个类
# 如果缺少PropertySourcesProcessor的Bean则生成ConfigPropertySourcesProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
# 根据配置是否在启动时加载 apollo.bootstrap.namespaces
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
@EnableApolloConfig: 通过@Import注解实现自动装配
@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;
}
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private final ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
helper.registerBeanDefinitions(importingClassMetadata, registry);
}
@Override
public void setEnvironment(Environment environment) {
this.helper.setEnvironment(environment);
}
}
// DefaultApolloConfigRegistrarHelper
/**
* Import导入时调用
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
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中,等待解析成Config
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);
// 设置PropertySources资源配置器,并将其优先级设置为最高
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
// Config解析成PropertySource放置到Environment中 并设置自动更新字段监听器
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),
PropertySourcesProcessor.class);
// Apollo注解处理器
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
ApolloAnnotationProcessor.class);
// Spring@Value注解处理器
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(),
SpringValueProcessor.class);
// XML处理器
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(),
SpringValueDefinitionProcessor.class);
}
// PropertySourcesProcessor 加载远程Config到Environment中并设置Config回调监听器
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.configUtil = ApolloInjector.getInstance(ConfigUtil.class);
// 初始化配置源
initializePropertySources();
// 设置自动更新
initializeAutoUpdatePropertiesFeature(beanFactory);
}
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();
for (ConfigPropertySource configPropertySource : configPropertySources) {
configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
}
}
// AutoUpdateConfigChangeListener Config监听器实现
/**
* Config级别的回调
*/
@Override
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
// 从springValueRegistry中获取相关SpringValue
// 通过@ApolloJsonValue @Value标注的字段和方法都会注册到SpringValueRegistry中
Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
if (targetValues == null || targetValues.isEmpty()) {
continue;
}
// 2. update the value
for (SpringValue val : targetValues) {
// 从PropertySourcesPlaceholderConfigurer中取值
// 实际上就是从PropertySourcesProcessor中设置的PropertySources中取值
updateSpringValue(val);
}
}
}