十二、Nacos源码系列:Nacos配置中心原理(四)- RefreshEvent 事件处理

前面文章,我们说到回调监听器的方法中,主要就是发布了一个RefreshEvent事件,这个事件主要由 SpringCloud 相关类来处理。今天我们继续分析后续的流程。

RefreshEvent 事件会由 RefreshEventListener 来处理,该 listener 含有一个 ContextRefresher 的对象。

public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationReadyEvent) {
        handle((ApplicationReadyEvent) event);
    }
    else if (event instanceof RefreshEvent) {
        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());
        // private ContextRefresher refresh
        Set<String> keys = this.refresh.refresh();
        log.info("Refresh keys changed: " + keys);
    }
}

// org.springframework.cloud.context.refresh.ContextRefresher#refresh
public synchronized Set<String> refresh() {
    // 刷新Environment环境信息
    Set<String> keys = refreshEnvironment();
    this.scope.refreshAll();
    return keys;
}

public synchronized Set<String> refreshEnvironment() {
    Map<String, Object> before = extract(
            this.context.getEnvironment().getPropertySources());
    // 添加配置内容到环境对象中
    addConfigFilesToEnvironment();
    Set<String> keys = changes(before,
            extract(this.context.getEnvironment().getPropertySources())).keySet();
    // 发布EnvironmentChangeEvent事件(环境变更事件)
    this.context.publishEvent(new EnvironmentChangeEvent事件(this.context, keys));
    return keys;
}

从源码可以看到,refreshEnvironment 会去刷新 Spring 环境变量,具体刷新思想就是重新创建一个Environment,然后将这个新的环境信息设置到原有的 Spring 环境中。拿到所有变化的配置项后,发布一个环境变化的 EnvironmentChangeEvent(环境变更事件)事件。

ConfigurationPropertiesRebinder 会监听 EnvironmentChangeEvent 事件,监听到事件后会对所有的标注有 ConfigurationProperties 注解的配置类进行销毁后重新初始化的操作,完成后我们的配置类中的属性就是最新的了。

// org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent
public void onApplicationEvent(EnvironmentChangeEvent event) {
    if (this.applicationContext.equals(event.getSource())
            // Backwards compatible
            || event.getKeys().equals(event.getSource())) {
        rebind();
    }
}

 

// 所有的标注有 ConfigurationProperties 注解的配置类
private ConfigurationPropertiesBeans beans;
public ConfigurationPropertiesRebinder(ConfigurationPropertiesBeans beans) {
    this.beans = beans;
}

public void rebind() {
    this.errors.clear();
    for (String name : this.beans.getBeanNames()) {
        rebind(name);
    }
}

public boolean rebind(String name) {
    if (!this.beans.getBeanNames().contains(name)) {
        return false;
    }
    if (this.applicationContext != null) {
        try {
            Object bean = this.applicationContext.getBean(name);
            if (AopUtils.isAopProxy(bean)) {
                bean = ProxyUtils.getTargetObject(bean);
            }
            if (bean != null) {
                // TODO: determine a more general approach to fix this.
                // see https://github.com/spring-cloud/spring-cloud-commons/issues/571
                if (getNeverRefreshable().contains(bean.getClass().getName())) {
                    return false; // ignore
                }

                // 销毁bean
                this.applicationContext.getAutowireCapableBeanFactory()
                        .destroyBean(bean);
                // 重新初始化bean
                this.applicationContext.getAutowireCapableBeanFactory()
                        .initializeBean(bean, name);
                return true;
            }
        }
        catch (RuntimeException e) {
            this.errors.put(name, e);
            throw e;
        }
        catch (Exception e) {
            this.errors.put(name, e);
            throw new IllegalStateException("Cannot rebind to " + name, e);
        }
    }
    return false;
}

上述代码只会对标有 ConfigurationProperties 注解的配置类进行rebind,那对于普通组件类里标有 @Value 注解的属性要怎么生效呢?这个其实需要配合 @RefreshScope 注解来生效的。

我们继续回到前面处理RefreshEvent事件的ContextRefresher#refresh()方法,接着会有一步 refreshAll 的操作,会调用父类的destroy()方法。

public synchronized Set<String> refresh() {
    Set<String> keys = refreshEnvironment();
    // private RefreshScope scope;
    this.scope.refreshAll();
    return keys;
}

// org.springframework.cloud.context.scope.refresh.RefreshScope#refreshAll
public void refreshAll() {
    // 调用父类的销毁方法:org.springframework.cloud.context.scope.GenericScope#destroy()
    super.destroy();
    this.context.publishEvent(new RefreshScopeRefreshedEvent());
}

父类就是 GenericScope,我们知道 Spring 中的 Bean 是有Scope 的概念的,Spring 默认 Scope 有单例和原型两种,同时提供了 Scope 扩展接口,通过实现该接口我们可以定义自己的 Scope。

在Spring IOC容器初始化的时候,doGetBean()方法中,这些自定义 Scope 类型对象的管理会交给相应的 Scope 实现去管理。

SpringCloud 实现的 RefreshScope 就是用来在运行时动态刷新 Bean 用的,RefreshScope 继承 GenericScope,提供 get()和 destroy()方法。

回到refreshAll()方法,在 refreshAll()中调用 super.destroy()方法时会将该 scope 的这些 Bean 都销毁掉,在下次 get()的时候会重新新触发spring的createBean,创建出一个新的bean对象,新创建的 Bean 就有了我们最新的配置。

// org.springframework.cloud.context.scope.GenericScope#get
public Object get(String name, ObjectFactory<?> objectFactory) {
    BeanLifecycleWrapper value = this.cache.put(name,
            new BeanLifecycleWrapper(name, objectFactory));
    this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
    try {
        // 会重新触发spring的createBean,创建出一个新的bean对象,填充进去的属性就是最新配置的内容
        return value.getBean();
    }
    catch (RuntimeException e) {
        this.errors.put(name, e);
        throw e;
    }
}

至此,我们就实现了配置的热更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/419522.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【YOLO系列】YOLOv9论文超详细解读(翻译 +学习笔记)

前言 时隔一年&#xff0c;YOLOv8还没捂热&#xff0c;YOLO系列最新版本——YOLOv9 终于闪亮登场&#xff01; YOLOv9的一作和v7一样。v4也有他。 他于2017年获得台湾省National Central University计算机科学与信息工程博士学位&#xff0c;现在就职于该省Academia Sinica的…

计算机二级MySQL-错题、知识点合集04

计算机二级MySQL 第四章 索引 主键约束&#xff0c;不允许为空也不允许重复。 NOT NULL非空约束属于自定义完整约束 PRIMARY KEY 属于实体完整性约束 FOREIGN KEY外键约束 外键与其引用的主键应分别属于不同的表&#xff0c;可以属于同一个关系&#xff1b;一个关系中可以定…

【java 基础】闲话 ClassLoader 和 SPI (一)

文章目录 引子双亲委派模型你真的明白了吗&#xff1f; 双亲委派“不够用了”SPI机制 其他琐碎 引子 有别于 java 提供的 IO 模块&#xff0c;java 中的classloader主要是用来加载类的&#xff0c;当然除了加载类&#xff0c;也可以加载资源文件。 那么首先我们会问一个问题&…

光伏业务管理软件有哪些推荐?

光伏业务管理软件是用于光伏电站的设计、施工、运营和维护等各个环节的软件工具。以下是一些推荐的光伏业务管理软件&#xff1a; PVsyst 这是一款全球广泛使用的光伏系统设计软件&#xff0c;可以进行详细的系统设计&#xff0c;包括组件匹配、逆变器选择、系统布局等。 鹧…

电子信息行业数字化转型创新应用挑战赛火热进行中,速戳

由深圳市宝安区人民政府、中国信息通信研究院联合举办的“第七届工业互联网数据创新应用大赛——解决方案赛道&#xff1a;电子信息行业数字化转型创新应用挑战赛”火热进行中&#xff01;大赛报名时间截至2024年3月15日&#xff0c;并将于3月25日在深圳宝安进行线下决赛答辩。…

如何辨别GPT3还是GPT4?

辨别后台使用的是GPT3还是GPT4可以提问以下问题验证&#xff1a; 1.昨天的当天的明天是哪天&#xff1f; 2.树上有9只鸟&#xff0c;猎人射杀了一只&#xff0c;还剩下多少只&#xff1f; 3.为什么周树人要打鲁迅&#xff1f; GPT3回答&#xff1a; GPT4回答&#xff1a; 如…

全闪存加速信创数据库数仓一体机解决方案

立足行业&#xff0c;深度解读 在新的大数据生态中&#xff0c;传统数据库/数据仓库技术和产品成为大数据生态中的组成部分&#xff0c;对结构化数据的存储和计算进行支撑。 数据库&数据仓库一体机是高端、核心数据管理产品&#xff0c;在我国党政、银行、交通等领域广泛…

市场热点袭来,直接加仓可靠吗?九方智投洪帮主助投资者明确几大要点

消费电子近期再度走强&#xff0c;多企业均出现涨停。消费电子出现乐观局面&#xff0c;有投资者认为是因为目前市场需求旺盛&#xff0c;产业链正在积极拉货&#xff0c;持续加单&#xff0c;各类手机新机也在积极备货&#xff0c;消费电子库存低。面对市场情绪高涨&#xff0…

LabVIEW最佳传输系统设计

LabVIEW最佳传输系统设计 介绍了基于LabVIEW软件开发的最佳基带传输系统和最佳带通传输系统的设计。通过软件仿真实现了脉冲成形滤波器和匹配滤波器的设计&#xff0c;证明了系统在消除码间干扰和抗噪声方面的优异性能。此设计不仅激发了学生的学习兴趣&#xff0c;还有助于提…

AI智能分析网关V4车辆违停算法在园区场景中的应用及特点

随着城市化进程的加速&#xff0c;车辆违停问题愈发严重&#xff0c;给城市交通带来了极大的困扰。为了解决这一问题&#xff0c;AI技术逐渐被应用于车辆违停的检测中。AI检测算法在车辆违停方面的应用&#xff0c;主要是通过计算机视觉技术&#xff0c;对道路上的车辆进行实时…

nodejs配置环境变量后不生效(‘node‘ 不是内部或外部命令,也不是可运行的程序或批处理文件)

一、在我们安装Node.js后&#xff0c;有时候会遇到node命令不管用的情况&#xff0c;关键是在安装时候已经添加配置了环境变量&#xff0c;向下面这样 但是还是不管用&#xff0c;这是因为环境变量配置不正确&#xff0c;权重不够&#xff0c;或者是命令冲突导致&#xff0c;解…

人工智能驱动的自拍时代:短视频美颜SDK技术的发展趋势

在短视频自拍的过程中&#xff0c;美颜技术的应用已经成为了许多人的必备工具&#xff0c;其中短视频美颜SDK技术的发展更是推动了自拍时代的进步。 1.人工智能技术的崛起 传统的美颜功能主要是通过简单的图像处理和滤镜效果来实现&#xff0c;但是这种方法往往会导致照片失真…

【Java】UWB高精度工业定位系统项目源代码

目录 UWB技术原理 优势 1. 高精度&#xff1a; 2. 抗干扰能力强&#xff1a; 3. 定位范围广&#xff1a; 4. 实时性强&#xff1a; 应用前景 定位系统源码功能介绍 实时定位&#xff1a; 轨迹回放&#xff1a; 区域管理&#xff1a; 巡检管理: 数据可视化分析&…

【ArcPy】批量读取文件夹excel中XY并转为点shp

示例展示 代码 只读取excel中含有XY字段的文件&#xff0c;并将矢量命名为excel文件名称。 import os import pandas as pd import arcpy folder_path r"C:\Users\admin\Desktop\excelfile" extension"xlsx" files [file for file in os.listdir(folder…

Prometheus(二):NodeExporter和Grafana的安装和使用

目录 1 Node Exporter安装1.1 简介1.2 安装1.3 Prometheus收集node_exporter数据 2 安装Grafana2.1 安装2.2 使用1、创建数据源2、选择模板3、模板导入 2.3 grafana创建用户1、创建用户2、验证 总结 1 Node Exporter安装 1.1 简介 node exporter是Prometheus的收集数据的组件…

使用 WordPress SureMembers 创建 Patreon 等粉丝网站

各行各业的创意专业人士和爱好者越来越多地转向在线平台分享他们的作品、建立忠实的粉丝并创造收入。 无论您是新晋艺术家还是知名影响者&#xff0c;拥有粉丝订阅网站都可以帮助您将热情货币化&#xff0c;同时培养紧密的支持者社区。 根据Statista的一份报告&#xff0c;到…

商城系统_大型商城系统源码_免费开源商城_OctShop

随着互联网对各行各业的渗透&#xff0c;以及人们网络购物的习惯已经形成&#xff0c;很多商家或企业都开始搭建与开发自己的商城系统平台。商城系统是一个以互联网技术为基础&#xff0c;依托电子商务的电商系统平台。一般大型的的商城系统都会有前台各种客户端&#xff0c;如…

【手机端测试】adb基础命令

一、什么是adb adb&#xff08;Android Debug Bridge&#xff09;是android sdk的一个工具 adb是用来连接安卓手机和PC端的桥梁&#xff0c;要有adb作为二者之间的维系&#xff0c;才能让用户在电脑上对手机进行全面的操作。 Android的初衷是用adb这样的一个工具来协助开发人…

PoC免写攻略

在网络安全领域&#xff0c;PoC&#xff08;Proof of Concept&#xff09;起着重要的作用&#xff0c;并且在安全研究、漏洞发现和漏洞利用等方面具有重要的地位。攻击方视角下&#xff0c;常常需要围绕 PoC 做的大量的工作。常常需要从手动测试开始编写 PoC&#xff0c;再到实…

【数仓】Hadoop软件安装及使用(集群配置)

一、环境准备 1、准备3台虚拟机 Hadoop131&#xff1a;192.168.56.131Hadoop132&#xff1a;192.168.56.132Hadoop133&#xff1a;192.168.56.133 本例系统版本 CentOS-7.8&#xff0c;已安装jdk1.8 2、hosts配置&#xff0c;关闭防火墙 vi /etc/hosts添加如下内容&#x…