大体思路是nacos 本地有一个定时任务(使用定时任务线程池实现),然后进行扫描,比较文件之前和之后的MD5 值,假如md5 值发生了变化,则进行前后文件的比较
首先通过类 “NacosContextRefresher”
@Override public void onApplicationEvent(ApplicationReadyEvent event) { // many Spring context if (this.ready.compareAndSet(false, true)) { this.registerNacosListenersForApplications(); } }
然后到 registerNacosListenersForApplications 方法
private void registerNacosListenersForApplications() { if (isRefreshEnabled()) { for (NacosPropertySource propertySource : NacosPropertySourceRepository .getAll()) { if (!propertySource.isRefreshable()) { continue; } String dataId = propertySource.getDataId(); registerNacosListener(propertySource.getGroup(), dataId); log.info("listening config: dataId={}, group={}", dataId, propertySource.getGroup()); } } }
再到
private void registerNacosListener(final String groupKey, final String dataKey) { String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey); Listener listener = listenerMap.computeIfAbsent(key, lst -> new AbstractSharedListener() { @Override public void innerReceive(String dataId, String group, String configInfo) { refreshCountIncrement(); nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo); // todo feature: support single refresh for listening applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); if (log.isDebugEnabled()) { log.debug(String.format( "Refresh Nacos config group=%s,dataId=%s,configInfo=%s", group, dataId, configInfo)); } } }); try { configService.addListener(dataKey, groupKey, listener); } catch (NacosException e) { log.warn(String.format( "register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey, groupKey), e); } }
其中重点关注 “applicationContext.publishEvent( new RefreshEvent(this, null, "Refresh Nacos config")); ”
这段代码,主要是发布事件,然后进入到类“AbstractApplicationContext” 中的
public void publishEvent(ApplicationEvent event) { publishEvent(event, null); }
再次进入到方法
protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } // Multicast right now if possible - or lazily once the multicaster is initialized if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
再次进入到从“getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);”方法进入到
类“SimpleApplicationEventMulticaster”的方法
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
然后走入到分支方法: invokeListener(listener, event); 再然后进入到方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); } }
在走入到分支:doInvokeListener(listener, event); 中再走入到方法
@SuppressWarnings({"rawtypes", "unchecked"}) private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass()) || (event instanceof PayloadApplicationEvent && matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception. Log loggerToUse = this.lazyLogger; if (loggerToUse == null) { loggerToUse = LogFactory.getLog(getClass()); this.lazyLogger = loggerToUse; } if (loggerToUse.isTraceEnabled()) { loggerToUse.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
走入到方法 listener.onApplicationEvent(event); 最后跳入到 类“RefreshEventListener”的方法:
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationReadyEvent) { handle((ApplicationReadyEvent) event); } else if (event instanceof RefreshEvent) { handle((RefreshEvent) event); } }
从分支 handle((RefreshEvent) event); 再跳入到
public void handle(RefreshEvent event) { if (this.ready.get()) { // don't handle events before app is ready log.debug("Event received " + event.getEventDesc()); Set<String> keys = this.refresh.refresh(); log.info("Refresh keys changed: " + keys); } }
这里我们需要重点关注 this.refresh 的 refresh()方法,先是进入到类 “ContextRefresher”的方法:
public synchronized Set<String> refresh() { Set<String> keys = refreshEnvironment(); this.scope.refreshAll(); return keys; }
然后重点来看 “refreshEnvironment” 方法
public synchronized Set<String> refreshEnvironment() { //这里的before 主要获取的是文件修改前的值 Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources()); // 刷新配置文件 updateEnvironment(); // 进行更新前后差异比较 Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); // 重复一遍上面的时间发布机制 this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys)); return keys; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构