Spring 之 MutablePropertyValues 和 ConstructorArgumentValues 的简单理解

news2024/11/25 18:57:17

1、MutablePropertyValues 概述

其实在绝大多情况下,MutablePropertyValues 这个类很少用,但是涉及到框架改造扩展可能就要使用到这个类。并且这个类在 BeanDefinition 模板中也是一个非常重要的角色。

id:Bean 唯一标识名称。
beanClass:类全限定名(包名+类名)。
init-method:定义 Bean 初始化方法,Bean 组装之后调用,必须是一个无参数方法。
destory-method:定义 Bean 销毁方法,在 BeanFactory 关闭时触发,同样也必须是一个无参构造方法,只能应用于 SingletonBean 单例 Bean。
factory-method:定义创建 Bean 对象的工厂方法,用于下面的 factory-bean,表示这个 Bean 是通过工厂方法创建,此时,class 属性 “失效”。
factory-bean:定义创建该 Bean 的工厂类,如果使用了 factory-bean,则 class 属性相当于 “失效”。
MultablePropertyValues:用于封装类属性的集合,里面是一个 List 容器,包装了很多 PropertyValue ,一个 PropertyValue 封装了一个属性及其对应的值,可以说一个属性及其值就是一个 PropertyValue,当我们需要再 BeanDefinition 中修改某个类里面的属性时就可以使用该类。
ConstructorArgumentValues:用来在 BeanDefinition 模版中指定使用哪个构造方法进行实例化 Bean,这个参数在集成 MyBatis 框架就使用到了。

那么这个类有什么作用呢?

目前据我了解到的这个类可以帮助我们在 BeanDefinition 中修改某个类的属性,下面就举个案例说明。

2、代码演示

先定义 ProcessorEntity 实体类,类里面有两个属性:name、birthday,两个构造函数,如下:


public class ProcessorEntity {

    private String name = "ABC";
    
    private Integer birthday;

	@Autowired
	public ProcessorEntity(Integer birthday) {
		System.out.println("birthdaybirthdaybirthdaybirthday");
	}

	public ProcessorEntity(String name) {
		System.out.println("namenamenamenamenam");
	}

	
	public Integer getBirth() {
		return birthday;
	}

	public void setBirth(Integer birthday) {
		this.birthday = birthday;
	}

	public String getName() {
		System.out.println("getName() 方法中的 scannerEntity = " + scannerEntity);
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

ProcessorEntity 类我们通过 BeanDefinitionRegistryPostProcessor 手动注册,这样也可以比较方便的对 BeanDefinition 进行修改。

定义一个 MyConfigurationPostProcessor1 类获取到 BeanDefinition 定义,可以借助 BeanDefinitionRegistryPostProcessor 接口,如下:


@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(ProcessorEntity.class);
		MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
		// 修改 ProcessorEntity 类中 name 属性默认值
		propertyValues.addPropertyValue("name","小明");
		
		// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
		registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
	}
}

代码中通过自己定制一个 GenericBeanDefinition,然后注册到 Spring 容器中,这样 Spring 就会按照设定好的模版生产 bean。

通过代码 genericBeanDefinition.getPropertyValues() 可以获取到 MutablePropertyValues 集合,源码如下:

在这里插入图片描述

容器拿到之后,假设现在要对 ProcessorEntity 类的属性 name 赋值,或者说是修改,应该怎么做呢?

可以将属性 name 和要设置的值封装成 PropertyValue,然后添加到 MutablePropertyValues 容器中即可,这样 Spring 自动帮咱们实现属性的设置。

其中代码 propertyValues.addPropertyValue("name","小明") 就是将 name=小明 封装到 PropertyValue,并添加到 MutablePropertyValues 容器,可以从源码看出,如下:

在这里插入图片描述在这里插入图片描述

最后测试结果如下:


public class TestBeanScanner {
	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(ScannerConfig.class);
		ProcessorEntity processorEntity = context.getBean("processorEntity", ProcessorEntity.class);	
		System.out.println("processorEntity = " + processorEntity+",name="+processorEntity.getName1());
	}
}

结果如下:

processorEntity = com.gwm.bean221207.processors.ProcessorEntity@20ccf40b,name=小明

从结果可以发现,name 默认值是 “ABC”,最终被修改成了 “小明”。

name 属性修改完成了,现在又想将 ProcessorEntity 类另一个属性 birthday 也修改下,我们肯定以为那还不简单,直接写代码,如下所示:


@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(ProcessorEntity.class);
		MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
		// 修改 ProcessorEntity 类中 name 属性默认值
		propertyValues.addPropertyValue("name","小明");
	    // 修改 ProcessorEntity 类中 birthday 属性默认值
	    propertyValues.addPropertyValue("birthday",2022);
	
		// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
		registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
	}
}

测试如下:

public class TestBeanScanner {
	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(ScannerConfig.class);
		ProcessorEntity processorEntity = context.getBean("processorEntity", ProcessorEntity.class);
		System.out.println("processorEntity = " + processorEntity+",name="+processorEntity.getName());
		System.out.println("processorEntity = " + processorEntity+",birthday="+processorEntity.getBirth());
	}
}


结果如下:

在这里插入图片描述

发现抛出异常,奇怪,检查上面的代码,发现属性名称是叫做 birthday,GenericBeanDefinition 也确实是为 birthday 属性赋值了呀,应该没问题的,但是细心的朋友应该发现了,name 和 birthday 两个属性,name 的 getXxx() 、setXxx() 中 Xxx 和 name 的驼峰写法一样,但是 birthday 驼峰和 birth 完全不一样。

说到这儿了,大家应该能推测出,这个 MutablePropertyValues 的更新操作应该是调用了 setXxx() 方法去实现的,那么最好的验证就是在 setName() 方法里面打上端点 debug ,如下:

在这里插入图片描述

看调用栈及参数显示,可以发发现最终是调用 setName() 方法进行属性操作的,所以在 GenericBeanDefinition 中的 MutablePropertyValues 容器中属性名称要和 setXxx() 中的 Xxx 一样。所以我们需要将 MutablePropertyValues 中的 birthday 修改成和 setBirth() 方法中的 birth 一样,如下:


@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(ProcessorEntity.class);
		MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
		// 修改 ProcessorEntity 类中 name 属性默认值
		propertyValues.addPropertyValue("name","小明");

		propertyValues.addPropertyValue("birth",2022);

		// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
		registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
	}
}

其实继续往深处源码看,会发现,它是先收集本类中所有方法,然后把 set、和 get 截取掉,截取处理之后,首字母转换成小写,最终将这个方法名称作为 key 封装成一个个的 PropertyInfo 如下:

在这里插入图片描述

所以我们在 MutablePropertyValues 中添加的名称,如果在 ProcessorEntity 类中能够找到一个对应的 setXxx() 方法,那么这个方法就会被调用,现在给 ProcessorEntity 添加一个方法 setSex() 方法,但是类中并没有 sex 这个属性,如下:


public class ProcessorEntity {

    private String name = "ABC";
    
    private Integer birthday;

	@Autowired
	public ProcessorEntity(Integer birthday) {
		System.out.println("birthdaybirthdaybirthdaybirthday");
	}

	public ProcessorEntity(String name) {
		System.out.println("namenamenamenamenam");
	}

    public Object setSex(String sex) {
		System.out.println("sex ====>"+sex);
		return new Object();
	}
	
	public Integer getBirth() {
		return birthday;
	}

	public void setBirth(Integer birthday) {
		this.birthday = birthday;
	}

	public String getName() {
		System.out.println("getName() 方法中的 scannerEntity = " + scannerEntity);
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

然后再 MutablePropertyValues 中调用并赋值,如下:


@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(ProcessorEntity.class);
		MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
		// 修改 ProcessorEntity 类中 name 属性默认值
		propertyValues.addPropertyValue("name","小明");

		propertyValues.addPropertyValue("birth",2022);

		propertyValues.addPropertyValue("sex","女");
		
		// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
		registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
	}
}

测试如下:

public class TestBeanScanner {
	public static void main(String[] args) {
		ApplicationContext context = new AnnotationConfigApplicationContext(ScannerConfig.class);
		ProcessorEntity processorEntity = context.getBean("processorEntity", ProcessorEntity.class);
		System.out.println("processorEntity = " + processorEntity+",name="+processorEntity.getName());
		System.out.println("processorEntity = " + processorEntity+",birthday="+processorEntity.getBirth());
	}
}

发现 setSex() 被调用了,结果也正常输出。如下:

sex ====>女
processorEntity = com.gwm.bean221207.processors.ProcessorEntity@32b260fa,name=abcdddddd
processorEntity = com.gwm.bean221207.processors.ProcessorEntity@32b260fa,birthday=2022

3、ConstructorArgumentValues 指定构造方法实例化 Bean

上面讲完了 MutablePropertyValues 类的作用,现在继续讲解下 ConstructorArgumentValues 的作用,这个作用就非常清晰了,就是可以再 BeanDefinition 创建的时候,指定使用哪个构造方法实例化 bean。

开局我们在 ProcessorEntity 类中准备好了两个构造方法,一个参数是 String,一个是 Integer,现在指定用 Integer 参数的构造方法实例化 bean,代码如下:



@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(ProcessorEntity.class);
		MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
		// 修改 ProcessorEntity 类中 name 属性默认值
		propertyValues.addPropertyValue("name","小明");

		propertyValues.addPropertyValue("birth",2022);

		propertyValues.addPropertyValue("sex","女");

		ConstructorArgumentValues constructorArgumentValues = genericBeanDefinition.getConstructorArgumentValues();
		constructorArgumentValues.addGenericArgumentValue(18);

		// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
		registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
	}
}

通过 genericBeanDefinition.getConstructorArgumentValues() 代码可以获取到 ConstructorArgumentValues 容器,源码如下:

在这里插入图片描述

其实也是一个 List 容器,需要使用哪个构造方法,就对应将入参值添加到对应的参数上即可,constructorArgumentValues.addGenericArgumentValue(18) 源码如下:

在这里插入图片描述

我们传入给构造方法参数的值被封装成一个个 ValueHolder 对象,并被添加到了 ConstructorArgumentValues 容器中。调用的源码如下:

在这里插入图片描述

注意这里有个判断条件 mbd.hasConstructorArgumentValues() 这里肯定是成立,因为在 MyConfigurationPostProcessor1 类中已经添加参数值。

在这里插入图片描述在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年全国职业院校技能大赛中职组网络安全竞赛试题 ——A模块(超详细解析)

2022年全国职业院校技能大赛(中职组) 网络安全竞赛试题(总分100分) 竞赛内容 模块A 基础设施设置与安全加固 (本模块20分) 一、项目和任务描述: 假定你是某企业的网络安全工程师&#xff0…

Python真的能杀死Excel吗?它能实现哪些Excel功能?

在大家的印象里,想进入金融行业或者数据岗位,首先需要精通Excel。而且现在招聘条件也是明确表示,要精通Excel等办公软件,后面还会加一句“有Python经验的优先”。 野村证券副首席数字官马修汉普森在上周五的伦敦Quant Conferenc…

(附源码)Springboot卫生院儿童预防接种平台 毕业设计 011404

springboot卫生院儿童预防接种平台 摘 要 卫生院儿童预防预接种工作实行网络信息化管理,是我国免疫规划工作发展的需要。儿童接种信息实行网络信息化不仅是预防接种工作步入了一个新的台阶,更重要的是解决了多年来流动儿童的免疫接种剂次不清&#xff0c…

(六)温故知新系列之RXJS——RXJS操作符基础(转化类)

前言 合并类操作符把多个数据流汇合为⼀个数据 流,但是汇合之前数据是怎样,在汇合之后还是那样;过滤类操作符可以 筛选掉⼀些数据,其中回压控制的过滤类操作符还可以改变数据传递给下 游的时间,但是数据本⾝不会变化&…

[附源码]计算机毕业设计Node.js茶叶销售网站(程序+LW)

项目运行 环境配置: Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境:最好是Nodejs最新版,我…

Java项目:基于jsp+mysql+Spring+mybatis的SSM在线网络图书商城

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台,有管理员与用户两种角色; 管理员角色包含以下功能: 管理员登录,商品分类管理,商品管理,商…

第十二章过滤器Fliter

文章目录什么是过滤器过滤器三要素过滤器的实例过滤器的匹配规则精确匹配模糊匹配前杠后星前星后缀创建一组img标签创建Filter前杠后缀,星号在中间匹配Servlet名称过滤器链Filter生命周期什么是过滤器 过滤器实际上就是对web资源进行拦截,做一些处理后再…

学术报告系列(八) - Fault-tolerant control of unmanned aerial vehicles

💂 个人主页: 同学来啦🤟 版权: 本文由【同学来啦】原创、在CSDN首发、需要转载请联系博主 💬 如果文章对你有帮助,欢迎关注、点赞、收藏和订阅专栏哦 🧑‍🔬 报告声明:资料整理于 ICARCE 2022 …

[附源码]计算机毕业设计Python的玉石交易系统(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等…

Web APIs 获取元素、操作元素和事件基础

1、Web API介绍 1.1、API的概念 API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码&#xff0c…

超实用的企业公众号运营方案分享,建议收藏

互联网时代,公众号几乎成为了企业的标配,一个企业没有公众号,只能说明这个企业不懂得宣传,公众号基于微信拥有庞大的用户流量,能帮助企业引流更多的潜在客户。 公众号运营是一门需要潜心钻研的学问,尤其是…

Power BI 数据导入(SQL Server、MySQL、网页数据)

一、数据源类型 POWER BI 可以连接多种数据源,包括: 文件(文本、CSV、Excel)数据库(SQL Server、MySQL、Azure Cosmos DB等NoSQL数据库)Power Platform和Azure(都是微软的数据产品/云平台)联机服务(Google Analytics…

为Azure SQLMI(Azure SQL 托管实例) 创建容器并开启审核日志

目录 (一)前言 (二)正文 1. 创建容器 (1)搜索到存储账户类型资源 (2)点击进入存储账户 (3)点击BLOB服务 (4)新建容器 2. 配置…

作为一名合格的React前端,必知window.event与SyntheticEvent的区别

通过鼠标的滚动来实现图片的放大和缩小,没错,像我这样不思进取的小白,从来不会自己动手撸一遍。 主要的功能函数: 一开始还挺纳闷为什么没有传入event参数也可以在函数中访问到,想起曾经是要传入一个event/e的呀&…

销售团队怎么管理?

销售团队怎么管理?这个问题真的很大,不同的销售团队管理方式也不相同。 但我们还是可以总结出一些常用的方法论: 不同销售结构的团队怎么管理不同发展阶段的销售团队如何管理团队管理的核心在于管人 内容略多,已经整理装订成册&…

22个最流行的三维重建软件【2022】

摄影测量是一种通过从照片创建 3D 模型来获取环境中真实世界对象的可靠数据的技术。从图像中提取 2D 和 3D 数据,并与对象、建筑物或地形的重叠照片一起转换为数字 3D 模型。 这允许捕捉大型物体,甚至是风景,否则无法扫描。 因此,…

C++之多态(上篇)(最全总结)

本篇目录前言1.多态的概念1.1概念2.多态的定义及实现2.1 虚函数2.2 虚函数的重写2.3 多态的构成条件3.一道经典题目4.多态的原理4.1虚函数表前言 需要声明的,这两篇文章(C之多态(上下篇))的运行环境都是在vs2013下的x…

Python数据可视化操作原理

后端是处理数据提取用户想要的数据。简单常用的是Python,相对于java,c, c,Python简直对初学者太友好,提供丰富多彩的API接口,比如常见的降维聚类算法:PCA, t-SNE, MDS, k-means等。如果用c实现过PCA算法有几…

MinGW下载和安装详细步骤 及 环境配置

一、下载 点击 这里 进入官网下载最新版本的MinGW。(这里下载的是Windows32位,但MinGW的所有软件都将在64位Windows平台上执行,所以32位和64位都是一样的。) 二、安装 1. 下载完成后,双击程序进行安装; …

[附源码]计算机毕业设计Python的连锁药店销售管理系统(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等…