记一次nacos自动更新导致druird属性数据更新异常

2023-11-11

现象

在这里插入图片描述

org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'spring.druid' to javax.sql.DataSource
	at org.springframework.boot.context.properties.bind.Binder.handleBindError(Binder.java:363)
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:323)
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:308)
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:238)

主要报错信息如下:

  • java.lang.IllegalStateException: Unable to set value for property initial-size
  • org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under ‘spring.druid’ to javax.sql.DataSource
    在nacos上修改了druid的属性配置后, 基于nacos的自动更新, 会重新设置druid的属性值

原因

public void setInitialSize(int initialSize) {
        if (this.initialSize == initialSize) {
            return;
        }

        if (inited) {
            throw new UnsupportedOperationException();
        }

        this.initialSize = initialSize;
    }

在重新绑定属性值的时候, 会调用对应的setter方法, 从上面报错的截图可以看到, 由于inited是false, 导致抛出了UnsupportedOperationException异常
inited属性默认是

protected volatile boolean                         inited                                    = false;

第一种就是把inited配置成true, 第二个就是自己重写DruidDataSourceWrapper实现自己的自动更新业务逻辑.

自动更新原理

先上图, 对流程有个整体的认识, 这个过程相对于spring容器启动的时候, 和创建bean的时候相差无几, 只是BeanPostProcessor不同而已.
在这里插入图片描述

在nacos变更数据后, 主要是通过发送event事件, 监听者监听事件变动后, 做相应的业务逻辑处理.

NacosContextRefresher

private void registerNacosListener(final String groupKey, final String dataKey) {
		Listener listener = listenerMap.computeIfAbsent(key,
				lst -> new AbstractSharedListener() {
					@Override
					public void innerReceive(String dataId, String group,
							String configInfo) {
						//发送RefreshEvent事件
						applicationContext.publishEvent(
								new RefreshEvent(this, null, "Refresh Nacos config"));
						if (log.isDebugEnabled()) {
					}
				});
		try {
			configService.addListener(dataKey, groupKey, listener);
		}
		catch (NacosException e) {
		}
	}

RefreshEventListener

public void handle(RefreshEvent event) {
		if (this.ready.get()) {
			//调用refresher的refresh方法
			Set<String> keys = this.refresh.refresh();
		}
	}

ContextRefresher

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;
	}

ConfigurationPropertiesRebinder

它是EnvironmentChangeEvent的监听器实现, 从名字上我们也可以看到, 它的主要作用是为了ConfigurationProperties配置方式的属性重新绑定.在nacos更新以后, 就是通过它做属性重新绑定的.

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

调用指定bean的rebind方法

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) {
					//省略....
					//获取bean实例, 首先销毁它, 然后重新初始化这个bean
					this.applicationContext.getAutowireCapableBeanFactory()
							.initializeBean(bean, name);
					return true;
				}
			}
			//省略...
		}
		return false;
	}

AbstractAutowireCapableBeanFactory

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		//省略...
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			//调用bean的前置处理器, 初始化数据, 调用processor的postProcessBeforeInitialization方法
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
		//省略...

		return wrappedBean;
	}

这里调用的processor就是ConfigurationPropertiesBindingPostProcessor

ConfigurationPropertiesBindingPostProcessor

private void bind(ConfigurationPropertiesBean bean) {
		//省略...
		try {
			this.binder.bind(bean);
		}
		catch (Exception ex) {
			throw new ConfigurationPropertiesBindException(bean, ex);
		}
	}

Binder

javabean的属性绑定就是通过它实现的.

private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler,
			Context context, boolean allowRecursiveBinding) {
		//省略...
		DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),
				propertyTarget, handler, context, false, false);
		return context.withDataObject(type, () -> {
			for (DataObjectBinder dataObjectBinder : this.dataObjectBinders) {
				Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);
				if (instance != null) {
					return instance;
				}
			}
			return null;
		});
	}

最终调用DataObjectBinder的bind方法完成属性的装配, DataObjectBinder的实现类如下:
在这里插入图片描述

JavaBean是用来将数据绑定到javabean对象上的工具
ValueObjectBinder是用来将数据绑定到值对象上的工具

这里我们简单看下JavaBean对象的数据绑定, 比如我们controller上的body的请求参数, 自动绑定到我们设置的javabean对象上, 就是通过它来实现的.

JavaBeanBinder

private <T> boolean bind(BeanSupplier<T> beanSupplier, DataObjectPropertyBinder propertyBinder,
			BeanProperty property) {
		//javabean的属性名称
		String propertyName = property.getName();
		//javabean的属性数据类型
		ResolvableType type = property.getType();
		//javabean的属性值
		Supplier<Object> value = property.getValue(beanSupplier);
		//javabean属性的注解
		Annotation[] annotations = property.getAnnotations();
		//获取属性值
		Object bound = propertyBinder.bindProperty(propertyName,
				Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
		if (bound == null) {
			return false;
		}
		//设置javabean的属性值
		if (property.isSettable()) {
			property.setValue(beanSupplier, bound);
		}
		//省略.....
	}

属性设置

void setValue(Supplier<?> instance, Object value) {
			try {
				this.setter.setAccessible(true);
				this.setter.invoke(instance.get(), value);
			}
			catch (Exception ex) {
				throw new IllegalStateException("Unable to set value for property " + this.name, ex);
			}
		}

这段代码是不是很熟悉, 写过反射的同学, 应该都知道这段代码的含义, 通过反射, 调用bean的setter方法, 赋值属性值.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

记一次nacos自动更新导致druird属性数据更新异常 的相关文章

  • Java 枚举与创建位掩码和检查权限的混淆

    我想将此 c 权限模块移植到 java 但是当我无法将数值保存在数据库中然后将其转换为枚举表示形式时 我很困惑如何执行此操作 在 C 中 我创建一个如下所示的枚举 public enum ArticlePermission CanRead
  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • Hibernate 挂起或引发延迟初始化,没有会话或会话已关闭

    我正在增强旧的 Spring Hibernate 应用程序 但我陷入困境 我有一种方法可以读取 3000 多行长的文件 每行都有一条记录 必须与数据库中的某些内容进行比较 然后必须将寄存器添加到数据库 多对多表 表和关系是 Branch h
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 内部类的构造函数引用在运行时失败并出现VerifyError

    我正在使用 lambda 为内部类构造函数创建供应商ctx gt new SpectatorSwitcher ctx IntelliJ建议我将其更改为SpectatorSwitcher new反而 SpectatorSwitcher 是我正
  • Spring Boot Data JPA 从存储过程接收多个输出参数

    我尝试通过 Spring Boot Data JPA v2 2 6 调用具有多个输出参数的存储过程 但收到错误 DEBUG http nio 8080 exec 1 org hibernate engine jdbc spi SqlStat
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • 非 Spring 托管类中 DI 的编译时编织

    我想为标记为的类配置编译时编织 Configurable注释能够将 spring 依赖项注入到初始化的类中new操作员 我不想使用加载时编织 因为我无权访问应用程序服务器的运行脚本 因此无法修改它 另外 我希望能够在测试中使用此类 我的意思
  • Android:无法使用 DbHelper 和 Contract 类将数据插入 SQLite

    public class Main2Activity extends AppCompatActivity private EditText editText1 editText2 editText3 editText4 private Bu
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 包 javax.el 不存在

    我正在使用 jre6 eclipse 并导入 javax el 错误 包 javax el 不存在 javac 导入 javax el 过来 这不应该是java的一部分吗 谁能告诉我为什么会这样 谢谢 米 EL 统一表达语言 是 Java
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url

随机推荐

  • flutter 修改host文件

    找地址 11 打开 https www ipaddress com 输入访问不了的域名 1 通过网站查网站ip地址 网上有很多网站可以查询网站的ip地址 这里推荐一个好的查询网站ip地址的网站 就是https www wanshangdat
  • extends与implements的使用和区别

    extends 是继承父类 只要那个类不是声明final或者定义为abstract就能继承 JAVA中不支持多重继承 继承只能继承一个类 但implements可以实现多个接口 用逗号分开就行了 比如 class A extends B i
  • datagrid动态更改属性值:如单选多选

    if staffName indexOf sendstaff 0 grid datagrid singleSelect true else grid datagrid singleSelect false
  • VScode User Settings

    1 How to find setting file gt preference gt setting 2 find the settings json 3 pay attention to the character after each
  • Java中通过NetworkInterface获取主机地址和物理地址等

    场景 Networklnterface类表示一个由名称和分配给此接口的IP地址列表组成的网络接口 也 就是Networklnterface类包含网络接口名称与IP地址列表 该类提供访问网卡设备的相关 信息 如可以获取网卡名称 IP地址和子网
  • sar命令

    sar 使用举例 1 输出CPU使用情况的统计信息 2 显示I O和传送速率的统计信息 3 输出内存页面的统计信息 4 输出每秒创建的进程数的进程统计信息 5 输出网络设备状态的统计信息 6 输出网络设备状态的统计信息 查看网络设备故障 7
  • unity前端通过java后端实现短信验证码登录

    一 搭建java后端 1 新建一个springboot项目 初始导入spring boot starter data redis spring boot starter data web lombok依赖 2 进入阿里巴巴短信运营商购买短信
  • 数据结构与算法--树的查找

    树的查找 当用线性表作为表的组织形式时 可以有三种查找法 其中二分查找效率最高 但由于二分查找要求表中结点按关键字有序 且不能用链表作存储结构 因此 当表的插入或删除操作频繁时 为维护表的有序性 势必要移动表中很多结点 这种由移动结点引起的
  • Jmeter系列-控制器Controllers的介绍(8)

    Controllers 简介 JMeter是一款功能强大的性能测试工具 而控制器是JMeter中非常重要的一个组件 控制器用于控制测试计划的执行流程 可以根据需求来控制线程的启动 停止 循环等操作 Jmeter有两种类型的控制器 Sampl
  • JS中setAttribute的使用

    在web开发中 经常会为某个标签设置属性 那么就可以利用js的setAttribute 方法为标签的属性设置值 下面的内容翻译自W3C DOM Level 1中关于setAttribute方法的说明 具体的方法参数如下 object set
  • 如何在MacOS下安装Python3

    对于Python开发者来讲 安装多个版本Python非常常见 原来我写过如何在Linux下安装Python3 今天我们来看下如何在MacOS下安装Python3 mac系统自带python 不过mac系统自带的python版本都是2 x版本
  • css伪类元素实现小圆点效果

    前言 使用伪类元素 before after 来实现 小圆点效果 效果图 实现方式 1 父级元素 postion relative 定位属性 可为absolute 必须 padding left 10px 因为伪类样式一般是在父级附近 根据
  • 2021-08-26

    代码块 地址引用与值引用 python机制的问题 默认地址引用而非值引用 ans append stk 的话 后面修改stk 会使得ans中的值变化 ans stk 1 2 3 ans append stk 此时 ans 1 2 3 stk
  • Pyecharts数据可视化之折线图(阶梯图、平滑曲线图、面积图)、K线图、常用配置项

    安装pyecharts pip install pyecharts U 本次使用jupyter notebook编写代码 折线图 引入相关包 from pyecharts faker import Faker faker数据构造器 from
  • 从创意到实现!GitMind AI一键生成思维导图,让您的灵感瞬间化作可视化成果!

    GitMind AI 一键生成思维导图 提高生产力效率 GitMind AI介绍 作为一款国产思维导图软件 GitMind已经在市场上获得了广泛的认可和使用 它的不断升级和更新也使得它越来越强大和好用 其中 最新推出的GitMind AI更
  • 《实时渲染》

    实时渲染 第四版
  • 【经典排序算法】3. 插入排序

    对顺序性强的数据 插入排序就比较快 最好情况O n 代码如下 public class Main public static void main String args int arr 3 3 5 6 2 1 arrPrint arr In
  • 后台输出二维码流并展示在前端页面

    后台生成了二维码 现需要将生成的二维码展示到前端页面 为了实现这个功能 在网上找了很久 大多都是建议使用window URL createObjectURL 贴上代码 let oid 934a1fca dc1d 4fe5 888b 23c7
  • 吴恩达深度学习 —— 3.10 直观理解反向传播

    z 1 W
  • 记一次nacos自动更新导致druird属性数据更新异常

    现象 org springframework boot context properties bind BindException Failed to bind properties under spring druid to javax