nacos-config-spring-boot-start (2.0.2)源码解析
思路
nacos 作为配置中心,其实实现分为两部分,一是位于我们自己服务的客户端, 二是服务端
一、客户端(client)
springboot 使用nacos配置中心 需要引入jar包 nacos-config-spring-boot-start,所以先从nacos-config-spring-boot-start 的jar 做为入口分析。
-
查看jar包结构如下,只有一个jar和依赖啥代码也没有,(初次看到此处的时候有点小蒙圈,咋和平时的不太一样),说明实现放在了其他依赖的jar 里面, 通过spom.xml文件的依赖关系以及pring.provider(无实际作用,提示和规范)文件里面的内容可以看出,其作为配置中心的实现主要放在nacos-config-spring-boot-autoconfigure,
-
重头戏:nacos-config-spring-boot-autoconfigure这个jar,发现里面终于有了源码,快速定位,spring.factories文件
看启动是后加载了那些bean
spring.factorise内容如下,主要包含几个bean,初看比较奇怪点就是为啥NacosConfigApplicationContextInitializer被注释掉了 ,(此处小坑)
NacosConfigAutoConfiguration
NacosConfigEnvironmentProcessor
NacosLoggingListener
#还有一个被注释掉的但是最重要的类,
#NacosConfigApplicationContextInitializer
挨个看启动自动装配的三个bean 做了什么事情
1.NacosConfigAutoConfiguration
主要是通过@Import注解导入了一个类NacosConfigBootBeanDefinitionRegistrar
@ConditionalOnProperty(name = NacosConfigConstants.ENABLED, matchIfMissing = true)
@ConditionalOnMissingBean(name = CONFIG_GLOBAL_NACOS_PROPERTIES_BEAN_NAME)
@EnableConfigurationProperties(value = NacosConfigProperties.class)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
@Import(value = { NacosConfigBootBeanDefinitionRegistrar.class })
@EnableNacosConfig
public class NacosConfigAutoConfiguration {
}
1.1NacosConfigBootBeanDefinitionRegistrar
NacosConfigBootBeanDefinitionRegistrar 主要实现了一下 BeanFactoryAware,ImportBeanDefinitionRegistrar,(动态加载和注册bean)
其实目的主要是为了注册:NacosBootConfigurationPropertiesBinder这个类也非常关键,主要是用于配置的属性与我们自定的bean
的绑定与更新
@Configuration
public class NacosConfigBootBeanDefinitionRegistrar
implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
//主要是为了注册NacosBootConfigurationPropertiesBinder
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(NacosBootConfigurationPropertiesBinder.class);
defaultListableBeanFactory.registerBeanDefinition(
NacosBootConfigurationPropertiesBinder.BEAN_NAME,
beanDefinitionBuilder.getBeanDefinition());
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
}
}
2.NacosConfigEnvironmentProcessor(环境处理器)
实现了EnvironmentPostProcessor,实现此接口可以在spring启动时候加载完环境配置后, 通过事件(ApplicationEnvironmentPreparedEvent)的方式最终调用postProcessEnvironment此方法自定义环境的处理
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
//注册NacosConfigApplicationContextInitializer这个bean
application.addInitializers(new NacosConfigApplicationContextInitializer(this));
nacosConfigProperties = NacosConfigPropertiesUtils
.buildNacosConfigProperties(environment);
if (enable()) {
System.out.println(
"[Nacos Config Boot] : The preload log configuration is enabled");
//加载配置
loadConfig(environment);
}
}
NacosConfigEnvironmentProcessor 主要是做了两件事情
2.1向spring 注册 NacosConfigApplicationContextInitializer
(此处前文就有提到,spring.factorie中就有此bean的配置但是被注释, 原来是此处将bean 注册的) 这bean非常的重要
将当前对象( EnvironmentPostProcessor) 通过构造方法传入 NacosConfigApplicationContextInitializer,因为
NacosConfigApplicationContextInitializer 需要用到nacoe的环境信息, 持有EnvironmentPostProcessor 对象用于执行自动刷新
第二个就是NacosConfigApplicationContextInitialer会注册DataCache的监听,用于配置变更时,修改enviroment中的值
2.2 主要是nacos配置环境的一些处理,加载和保存nacos配置,启动ClientWorker
这里有个非常重要的成员变量 private Function<Properties, ConfigService> builder
定义了获取和创建 ConfigService(用于获取服务端配置的类)这里实际返回的是ConfigSerive的子类NacosConfigService
而NacosConfigService创建的时候会初始化nacos的客户端ClientWorker.
ClientWorker里面代理一个ConfigRpcTransportClient会启动一个线程,创建与服务端的长连接,接受服务端的各种事件通知,其中就包括配置变更事件
public NacosConfigService(Properties properties) throws NacosException {
ValidatorUtils.checkInitParam(properties);
initNamespace(properties);
this.configFilterChainManager = new ConfigFilterChainManager(properties);
ServerListManager serverListManager = new ServerListManager(properties);
serverListManager.start();
//初始化nacos客户端,ClientWorker里面会启动一个线程,创建与服务端的长连接,接受服务端的各种事件通知,其中就包括配置变更事件
this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties);
// will be deleted in 2.0 later versions
agent = new ServerHttpAgent(serverListManager);
}
2.2.1NacosConfigLoader
其中locadConfig方法非常重要,主要用于加载服务端中nacos的配置
public class NacosConfigLoader {
private final Logger logger = LoggerFactory.getLogger(NacosConfigLoader.class);
private final NacosConfigProperties nacosConfigProperties;
private final ConfigurableEnvironment environment;
private Function<Properties, ConfigService> builder;
private List<DeferNacosPropertySource> nacosPropertySources = new LinkedList<>();
public NacosConfigLoader(NacosConfigProperties nacosConfigProperties,
ConfigurableEnvironment environment,
Function<Properties, ConfigService> builder) {
this.nacosConfigProperties = nacosConfigProperties;
this.environment = environment;
this.builder = builder;
}
public void loadConfig() {
//构建nacos远程配置请求需要的属性对象(ncose 地址,名称空间,用户信息...等)
Properties globalProperties = buildGlobalNacosProperties();
MutablePropertySources mutablePropertySources = environment.getPropertySources();
//获取远程配置
List<NacosPropertySource> sources = reqGlobalNacosConfig(globalProperties,
nacosConfigProperties.getType());
for (NacosConfigProperties.Config config : nacosConfigProperties.getExtConfig()) {
List<NacosPropertySource> elements = reqSubNacosConfig(config,
globalProperties, config.getType());
sources.addAll(elements);
}
if (nacosConfigProperties.isRemoteFirst()) {
for (ListIterator<NacosPropertySource> itr = sources.listIterator(sources.size()); itr.hasPrevious();) {
mutablePropertySources.addAfter(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, itr.previous());
}
} else {
for (NacosPropertySource propertySource : sources) {
mutablePropertySources.addLast(propertySource);
}
}
}
//从远程nacos 服务端根据dataId获取nacos的中的配置
private NacosPropertySource[] reqNacosConfig(Properties configProperties,
String[] dataIds, String groupId, ConfigType type, boolean isAutoRefresh) {
final NacosPropertySource[] propertySources = new NacosPropertySource[dataIds.length];
for (int i = 0; i < dataIds.length; i++) {
if (StringUtils.isEmpty(dataIds[i])) {
continue;
}
// Remove excess Spaces
final String dataId = environment.resolvePlaceholders(dataIds[i].trim());
final String config = NacosUtils.getContent(builder.apply(configProperties),
dataId, groupId);
final NacosPropertySource nacosPropertySource = new NacosPropertySource(
dataId, groupId,
buildDefaultPropertySourceName(dataId, groupId, configProperties),
config, type.getType());
nacosPropertySource.setDataId(dataId);
nacosPropertySource.setType(type.getType());
nacosPropertySource.setGroupId(groupId);
nacosPropertySource.setAutoRefreshed(isAutoRefresh);
logger.info("load config from nacos, data-id is : {}, group is : {}",
nacosPropertySource.getDataId(), nacosPropertySource.getGroupId());
propertySources[i] = nacosPropertySource;
DeferNacosPropertySource defer = new DeferNacosPropertySource(
nacosPropertySource, configProperties, environment);
nacosPropertySources.add(defer);
}
return propertySources;
}
}
3.ClientWorker
3.1startInternal
启动线程监听一个长度为1的阻塞队列,并且执行监听,当阻塞队列中有值时候立马执行配置监听, 当阻塞队列中无值5秒执行一次监听并通知监听者
listenExecutebell中加入值的情况l(istenExecutebell.offer):
1.service RPC通知
2.注册或者移除DtaCache监听器
public void startInternal() throws NacosException {
executor.schedule(new Runnable() {
@Override
public void run() {
while (!executor.isShutdown() && !executor.isTerminated()) {
try {
//当阻塞队列中有值时候立马执行配置监听, 当阻塞队列中无值5秒执行一次配置监听,并通知监听者
listenExecutebell.poll(5L, TimeUnit.SECONDS);
if (executor.isShutdown() || executor.isTerminated()) {
continue;
}
executeConfigListen();
} catch (Exception e) {
LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e);
}
}
}
}, 0L, TimeUnit.MILLISECONDS);
}
3.2executeConfigListen
executeConfigListen主要做了如下几个事情
1.向着Service注册或者移除DateCached的监听(requestProxy)
2.刷新发生变更的DataCache 的内容,自动刷新配置值(refreshContentAndCheck)
->ClienWork.refreshContentAndCheck
->CacheData.checkListenerMd5
->CacheData.safeNotifyListener
->DelegatingEventPublishingListener.receiveConfigInfo
->DelegatingEventPublishingListener.publishEvent ( 发送NacosConfigReceivedEvent)
->NacosValueAnnotationBeanPostProcessor.onApplicationEvent (接收NacosConfigReceivedEventm,刷新nacosValue注解上的值)
通知所有的CacheData变化的所有的监听者,处理监听事件
CacheData的监听者处理拿到最新的内容执行对应的逻辑, 当没有客户端主动通知或者监听者变更的时候5秒执行一次
比如NacosValue注解注册的实现
1.1.CacheData监听器拿到最新的配置值,刷新spring envoroment 中的值
1.2.发送配置变更事件,通过NacosValueAnnotationBeanPostProcessor 刷新配置属性值
public void executeConfigListen() {
Map<String, List<CacheData>> listenCachesMap = new HashMap<String, List<CacheData>>(16);
Map<String, List<CacheData>> removeListenCachesMap = new HashMap<String, List<CacheData>>(16);
long now = System.currentTimeMillis();
boolean needAllSync = now - lastAllSyncTime >= ALL_SYNC_INTERNAL;
//循环所有的CacheData 初始化listenCachesMap,(拿到所有CacheData注册的监听器)
for (CacheData cache : cacheMap.get().values()) {
//................................................
}
boolean hasChangedKeys = false;
//
if (!listenCachesMap.isEmpty()) {
for (Map.Entry<String, List<CacheData>> entry : listenCachesMap.entrySet()) {
String taskId = entry.getKey();
List<CacheData> listenCaches = entry.getValue();
//构建注册监听对象,
ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(listenCaches);
configChangeListenRequest.setListen(true);
try {
RpcClient rpcClient = ensureRpcClient(taskId);
//像nacos service 注册监听
ConfigChangeBatchListenResponse configChangeBatchListenResponse = (ConfigChangeBatchListenResponse) requestProxy(
rpcClient, configChangeListenRequest);
if (configChangeBatchListenResponse != null && configChangeBatchListenResponse.isSuccess()) {
Set<String> changeKeys = new HashSet<String>();
//handle changed keys,notify listener
if (!CollectionUtils.isEmpty(configChangeBatchListenResponse.getChangedConfigs())) {
hasChangedKeys = true;
for (ConfigChangeBatchListenResponse.ConfigContext changeConfig : configChangeBatchListenResponse
.getChangedConfigs()) {
String changeKey = GroupKey
.getKeyTenant(changeConfig.getDataId(), changeConfig.getGroup(),
changeConfig.getTenant());
changeKeys.add(changeKey);
boolean isInitializing = cacheMap.get().get(changeKey).isInitializing();
//刷新配置,->CacheData.safeNotifyListener 通知监听者执行监听回调
refreshContentAndCheck(changeKey, !isInitializing);
}
}
//handler content configs
for (CacheData cacheData : listenCaches) {
String groupKey = GroupKey
.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.getTenant());
if (!changeKeys.contains(groupKey)) {
//sync:cache data md5 = server md5 && cache data md5 = all listeners md5.
synchronized (cacheData) {
if (!cacheData.getListeners().isEmpty()) {
cacheData.setSyncWithServer(true);
continue;
}
}
}
cacheData.setInitializing(false);
}
}
} catch (Exception e) {
LOGGER.error("Async listen config change error ", e);
try {
Thread.sleep(50L);
} catch (InterruptedException interruptedException) {
//ignore
}
}
}
}
if (!removeListenCachesMap.isEmpty()) {
for (Map.Entry<String, List<CacheData>> entry : removeListenCachesMap.entrySet()) {
String taskId = entry.getKey();
List<CacheData> removeListenCaches = entry.getValue();
ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches);
configChangeListenRequest.setListen(false);
try {
RpcClient rpcClient = ensureRpcClient(taskId);
boolean removeSuccess = unListenConfigChange(rpcClient, configChangeListenRequest);
if (removeSuccess) {
for (CacheData cacheData : removeListenCaches) {
synchronized (cacheData) {
if (cacheData.getListeners().isEmpty()) {
ClientWorker.this
.removeCache(cacheData.dataId, cacheData.group, cacheData.tenant);
}
}
}
}
} catch (Exception e) {
LOGGER.error("async remove listen config change error ", e);
}
try {
Thread.sleep(50L);
} catch (InterruptedException interruptedException) {
//ignore
}
}
}
if (needAllSync) {
lastAllSyncTime = now;
}
//If has changed keys,notify re sync md5.
if (hasChangedKeys) {
notifyListenConfig();
}
}
2.代理了一个内部类ConfigRpcTransportClient, ConfigRpcTransportClient的功能主要是与客户端维持长连接,接受客户端的的各种RPC事件回调 ,启动ClientWork的时候会调用initRpcClientHandler方法 与nacose服务端建立长连接,nacos服务端如果配置发生变成会发送配置变更事件,异步处理配置变更,最终通过此长连接发送变更的配置内容
/**
* 此方法会注册各种事件处理器,处理不同的服务端的RPC请求
*/
private void initRpcClientHandler(final RpcClient rpcClientInner) {
/*
* 配置变更处理器
* Register Config Change /Config ReSync Handler
*/
rpcClientInner.registerServerRequestHandler((request) -> {
if (request instanceof ConfigChangeNotifyRequest) {
ConfigChangeNotifyRequest configChangeNotifyRequest = (ConfigChangeNotifyRequest) request;
LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}",
rpcClientInner.getName(), configChangeNotifyRequest.getDataId(),
configChangeNotifyRequest.getGroup(), configChangeNotifyRequest.getTenant());
String groupKey = GroupKey
.getKeyTenant(configChangeNotifyRequest.getDataId(), configChangeNotifyRequest.getGroup(),
configChangeNotifyRequest.getTenant());
CacheData cacheData = cacheMap.get().get(groupKey);
if (cacheData != null) {
cacheData.setSyncWithServer(false);
//触发事件通知,ClientWork的executeConfigListen立即执行
//更新@NacosValue注解值
notifyListenConfig();
}
return new ConfigChangeNotifyResponse();
}
return null;
});
rpcClientInner.registerServerRequestHandler((request) -> {
if (request instanceof ClientConfigMetricRequest) {
ClientConfigMetricResponse response = new ClientConfigMetricResponse();
response.setMetrics(getMetrics(((ClientConfigMetricRequest) request).getMetricsKeys()));
return response;
}
return null;
});
rpcClientInner.registerConnectionListener(new ConnectionEventListener() {
@Override
public void onConnected() {
LOGGER.info("[{}] Connected,notify listen context...", rpcClientInner.getName());
notifyListenConfig();
}
@Override
public void onDisConnect() {
String taskId = rpcClientInner.getLabels().get("taskId");
LOGGER.info("[{}] DisConnected,clear listen context...", rpcClientInner.getName());
Collection<CacheData> values = cacheMap.get().values();
for (CacheData cacheData : values) {
if (StringUtils.isNotBlank(taskId)) {
if (Integer.valueOf(taskId).equals(cacheData.getTaskId())) {
cacheData.setSyncWithServer(false);
}
} else {
cacheData.setSyncWithServer(false);
}
}
}
});
rpcClientInner.serverListFactory(new ServerListFactory() {
@Override
public String genNextServer() {
return ConfigRpcTransportClient.super.serverListManager.getNextServerAddr();
}
@Override
public String getCurrentServer() {
return ConfigRpcTransportClient.super.serverListManager.getCurrentServerAddr();
}
@Override
public List<String> getServerList() {
return ConfigRpcTransportClient.super.serverListManager.serverUrls;
}
});
NotifyCenter.registerSubscriber(new Subscriber() {
@Override
public void onEvent(Event event) {
rpcClientInner.onServerListChange();
}
@Override
public Class<? extends Event> subscribeType() {
return ServerlistChangeEvent.class;
}
});
}
另外一个非常重要的类是:NacosConfigLoader 用于从远程服务端获取配置
public class NacosConfigEnvironmentProcessor
implements EnvironmentPostProcessor, Ordered {
private final Logger logger = LoggerFactory
.getLogger(NacosConfigEnvironmentProcessor.class);
private final CacheableEventPublishingNacosServiceFactory nacosServiceFactory = CacheableEventPublishingNacosServiceFactory
.getSingleton();
private final Map<String, ConfigService> serviceCache = new HashMap<>(8);
//nacos配置中心属性的来源,项目中配置的dataId,对应一个DeferNacosPropertySource
//在执行loadConfig方法的时候初始化
private final LinkedList<NacosConfigLoader.DeferNacosPropertySource> deferPropertySources = new LinkedList<>();
private NacosConfigProperties nacosConfigProperties;
private Function<Properties, ConfigService> builder = properties -> {
try {
//获取缓存的key ,namespace,serverAddress,contextPath,clusterName,endpoint,accessKey,secretKey,encode
final String key = NacosUtils.identify(properties);
if (serviceCache.containsKey(key)) {
return serviceCache.get(key);
}
//通过反射的方式获取client包中的 ConfigService,并缓存,缓存的key为nacos的属性信息
final ConfigService configService = NacosFactory
.createConfigService(properties);
serviceCache.put(key, configService);
return nacosServiceFactory.deferCreateService(configService, properties);
}
catch (NacosException e) {
throw new NacosBootConfigException(
"ConfigService can't be created with properties : " + properties, e);
}
};
}
4.NacosConfigApplicationContextInitializer
前面说到在spring.factories 文件中,NacosConfigApplicationContextInitializer 被注释掉了,但是在NacosConfigEnvironmentProcessor 环境处理器中又注册了此类,猜测目的是为了让NacoseConfigApplicationContextInitializer初始化在NacosConfigEnvironmentProcessor 类之后,主要是从两个方面验证此猜想。
- 逻辑上NacosConfigEnvironmentProcessor 是处理和初始化环境相关的,需要放在第一步。
- NacosConfigApplicationContextInitializer 需要对象需要持有NacosConfigEnvironmentProcessor ,对环境进行监听和处理,所以NacosConfigApplicationContextInitializer 对象需要在NacosConfigEnvironmentProcessor初始化以后才能开始初始化。
public class NacosConfigApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private final Logger logger = LoggerFactory
.getLogger(NacosConfigApplicationContextInitializer.class);
private final NacosConfigEnvironmentProcessor processor;
private final CacheableEventPublishingNacosServiceFactory singleton = CacheableEventPublishingNacosServiceFactory
.getSingleton();
private final Function<Properties, ConfigService> builder = properties -> {
try {
return singleton.createConfigService(properties);
}
catch (NacosException e) {
throw new NacosBootConfigException(
"ConfigService can't be created with properties : " + properties, e);
}
};
private ConfigurableEnvironment environment;
private NacosConfigProperties nacosConfigProperties;
//构造方法,持有NacosConfigEnvironmentProcessor 对象
public NacosConfigApplicationContextInitializer(
NacosConfigEnvironmentProcessor configEnvironmentProcessor) {
this.processor = configEnvironmentProcessor;
}
因为NacosConfigApplicationContextInitializer 实现了ApplicationContextInitializer 接口。所以在spring 容器刷新的时候会执行其initialize方法,initialize主要是为了添加dataId配置(DeferNacosPropertySource)的监听器,也就是DateChe的监听器, ClientWork会默认5秒会回调一次需要执行的监听器,如遇到service主动通知会立即通知
configLoader.addListenerIfAutoRefreshed();
->NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed
->NacosConfigService.addListener
->ClientWorker.addTenantListeners
->ClientWorker.addCacheDataIfAbsent (创建和缓存CacheData,并且返回) cache.setSyncWithServer(false);
->CacheData.addListener (添加监听)
public void initialize(ConfigurableApplicationContext context) {
singleton.setApplicationContext(context);
environment = context.getEnvironment();
nacosConfigProperties = NacosConfigPropertiesUtils
.buildNacosConfigProperties(environment);
//创建NacosConfigLoader 配置加载器
final NacosConfigLoader configLoader = new NacosConfigLoader(
nacosConfigProperties, environment, builder);
if (!enable()) {
logger.info("[Nacos Config Boot] : The preload configuration is not enabled");
}
else {
// If it opens the log level loading directly will cache
// DeferNacosPropertySource release
if (processor.enable()) {
processor.publishDeferService(context);
configLoader
.addListenerIfAutoRefreshed(processor.getDeferPropertySources());
}
else {
configLoader.loadConfig();
//添加监听,最终走到NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed
//为每一个dataId添加一个监听器
//监听器中会将配置变更的内容通过MutablePropertySources的方式,将spring环境(environment)中的值进行替换
//后续,nacosValue注解后置处理器,监听处理的时候,也是直接从spring环境中取值替换
configLoader.addListenerIfAutoRefreshed();
}
}
final ConfigurableListableBeanFactory factory = context.getBeanFactory();
if (!factory
.containsSingleton(NacosBeanUtils.GLOBAL_NACOS_PROPERTIES_BEAN_NAME)) {
factory.registerSingleton(NacosBeanUtils.GLOBAL_NACOS_PROPERTIES_BEAN_NAME,
configLoader.buildGlobalNacosProperties());
}
}
4.1NacosPropertySourcePostProcessor
addListenerIfAutoRefreshed做了两件事情
1.定义监听(属性变化后,替换spring enveroment 的值,方便NacosValueAnnotationBeanPostProcessor 从环境取值替换)
2.注册监听
最终通过EventPublishingConfigService 注册 DelegatingEventPublishingListener
public static void addListenerIfAutoRefreshed(
final NacosPropertySource nacosPropertySource, final Properties properties,
final ConfigurableEnvironment environment) {
if (!nacosPropertySource.isAutoRefreshed()) { // Disable Auto-Refreshed
return;
}
final String dataId = nacosPropertySource.getDataId();
final String groupId = nacosPropertySource.getGroupId();
final String type = nacosPropertySource.getType();
final NacosServiceFactory nacosServiceFactory = getNacosServiceFactoryBean(
beanFactory);
try {
//这里创建的是 EventPublishingConfigService
ConfigService configService = nacosServiceFactory
.createConfigService(properties);
Listener listener = new AbstractListener() {
@Override
public void receiveConfigInfo(String config) {
String name = nacosPropertySource.getName();
NacosPropertySource newNacosPropertySource = new NacosPropertySource(
dataId, groupId, name, config, type);
newNacosPropertySource.copy(nacosPropertySource);
MutablePropertySources propertySources = environment
.getPropertySources();
// replace NacosPropertySource
propertySources.replace(name, newNacosPropertySource);
}
};
if (configService instanceof EventPublishingConfigService) {
((EventPublishingConfigService) configService).addListener(dataId,
groupId, type, listener);
}
else {
configService.addListener(dataId, groupId, listener);
}
}
catch (NacosException e) {
throw new RuntimeException(
"ConfigService can't add Listener with properties : " + properties,
e);
}
}
NacosValueAnnotationBeanPostProcessor
public class NacosValueAnnotationBeanPostProcessor
extends AbstractAnnotationBeanPostProcessor implements BeanFactoryAware,
EnvironmentAware, ApplicationListener<NacosConfigReceivedEvent> {
//监听配置变更事件,替换@NacosValue中更改的值
public void onApplicationEvent(NacosConfigReceivedEvent event) {
// In to this event receiver, the environment has been updated the
// latest configuration information, pull directly from the environment
// fix issue #142
for (Map.Entry<String, List<NacosValueTarget>> entry : placeholderNacosValueTargetMap
.entrySet()) {
String key = environment.resolvePlaceholders(entry.getKey());
String newValue = environment.getProperty(key);
if (newValue == null) {
continue;
}
List<NacosValueTarget> beanPropertyList = entry.getValue();
for (NacosValueTarget target : beanPropertyList) {
String md5String = MD5Utils.md5Hex(newValue, "UTF-8");
boolean isUpdate = !target.lastMD5.equals(md5String);
if (isUpdate) {
target.updateLastMD5(md5String);
Object evaluatedValue = resolveNotifyValue(target.nacosValueExpr, key, newValue);
if (target.method == null) {
setField(target, evaluatedValue);
}
else {
setMethod(target, evaluatedValue);
}
}
}
}
}
}