Spring 类型转换、数值绑定与验证(一)— DataBinder

 DataBinder 是Spring用于数据绑定、类型转换及验证的类。使用场景有:1)xml配置文件定义bean,Spring 内部使用DataBinder 来完成属性的绑定;2)Web请求参数绑定,在Spring MVC 中,Controller的方法参数通常会自动绑定到请求参数中,主要用DataBinder来完成。3)自定义数据绑定,可手动创建DataBinder 对象,为其设置校验器和转换器,来满足特定需求。

1 DataBinder 类

图 DataBinder UML图

PropertyEditorRegistry:

PropertyEditor 注册商,用来管理及保存PropertyEditor(JDK自带接口,支持各种不同的方式来显示和更新属性值,比如在Spring的xml中,配置Bean 时,把字符串类型转化成对应的Integer、File等类型)。定义了注册PropertyEditor 的接口。

TypeConverter:

定义了类型转化方法。

public class DataBinderTest {

    public static void main(String[] args) throws BindException {
        User user = new User();
        DataBinder binder = new DataBinder(user);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.add("username","黄先生");
        propertyValues.add("address.province","广东");
        propertyValues.add("address.city","深圳");
        propertyValues.add("infos['job']","程序员");
        propertyValues.add("age",28);
        binder.bind(propertyValues);
        System.out.println(user); // User{username='黄先生', age=28, address=Address{province='广东', city='深圳'}, infos={job=程序员}}
    }

    public static class User {
        private String username;
        private Integer age;
        private Address address;

        private Map<String,Object> infos;

        public void setUsername(String username) {
            this.username = username;
        }

        public String getUsername() {
            return username;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public Integer getAge() {
            return age;
        }

        public void setAddress(Address address) {
            this.address = address;
        }

        public Address getAddress() {
            return address;
        }

        public Map<String, Object> getInfos() {
            return infos;
        }

        public void setInfos(Map<String, Object> infos) {
            this.infos = infos;
        }

        @Override
        public String toString() {
            return "User{" +
                    "username='" + username + '\'' +
                    ", age=" + age +
                    ", address=" + address +
                    ", infos=" + infos +
                    '}';
        }
    }

    public static class Address {
        private String province;
        private String city;

        public void setProvince(String province) {
            this.province = province;
        }

        public String getProvince() {
            return province;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getCity() {
            return city;
        }

        @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    }
}

2 数值绑定过程

如上代码。使用了DataBinder的bind(PropertyValues pvs) 来将属性值绑定到目标对象中。

图 DataBinder的bind 方法

图 数据绑定过程中的方法调用

2.1 PropertyValues

PropertyValues 用于保存一个或多个Property的接口。MutablePropertyValues是其实现类。新增了对Property 新增、删除等操作。

2.1.1 Property

图 Property UML

AttributeAccessor: 定义了对象属性的访问及设置值等操作的接口。

AttributeAccessorSupport: 是AttributeAccessor 的实现类,这里的属性操作对象是一个Map类型。

BeanMetadataElement:定义了一个方法getSource(),用于获取Bean元数据的源对象。这个源对象通常是源文件信息。

BeanMetadataAttributeAccessor: 会覆盖AttributeAccessorSupport中设置和获取属性的方法,会将属性转换为BeanMetadataAttribute,目的是为了跟踪Bean定义的源对象。

BeanMetadataAttribute: 在Spring容器中,Bean的定义来源与多种不同的配置方式,例如XML、注解、配置类等。当Spring容器解析Bean的定义时,它会将源信息(如XML文件的路径、注解的位置等)与Bean的定义关联起来,这样,在后续处理中,如果需要对Bean的定义进行查找、修改或报告错误,Spring可以使用这个源信息。

Property:与BeanMetadataAttributeAccessor 不同的是,Property将属性和值封装在一个对象中,而不是使用Map对象来保存所有属性。这种设计提供了更大的灵活性和便利性,并允许处理索引属性以进行优化。

2.2 applyPropertyValues方法

在该方法中,执行下面语句来完成对目标对象的赋值:

getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());

getPropertyAccessor()方法:

protected ConfigurablePropertyAccessor getPropertyAccessor() {
    return getInternalBindingResult().getPropertyAccessor();
}

getInternalBindingResult()方法:

protected AbstractPropertyBindingResult getInternalBindingResult() {
    if (this.bindingResult == null) {
       initBeanPropertyAccess();
    }
    return this.bindingResult;
}

调用关系为:

AbstractPropertyBindingResult.getPropertyAccessor() -> ConfigurablePropertyAccessor.setPropertyValues(参数) 完成对象属性赋值。

2.2.1 AbstractPropertyBindingResult

数据绑定的结果。BindingResult用于处理数据绑定的错误。主要用来收集、存储和管理在数据绑定过程中发生的错误。

图 AbstractPropertyBindingResult UML

AbstractPorpertyBindingResult 是一个抽象类,定义了一个抽象方法:getPropertyAccessor。返回ConfigurablePropertyAccessor类型。有两个实现类。

DirectFieldBindingResult

用于能直接访问的对象,而不是通过getter和setter方法访问的。

BeanPropertyBindingResult

默认实现,标准bean,通过getter和setter方法访问的。

表 AbstractPropertyBindingResult两个实现类

图 BeanPropertyBindingResult的getPropertyAccessor方法

图 BeanPropertyBindingResult 类的代码

PropertyAccessorFactory 是一个简单的工厂类,根据对象属性是否直接访问来创建对应的ConfigurablePropertyAccessor。

2.2.2 ConfigurablePropertyAccessor

提供了灵活的方式来访问和操作Bean对象的属性,并支持类型转化和属性编辑等功能。该接口新增了设置及获取ConversionService的方法。

图 ConfigurablePropertyAccessor接口 UML

PropertyAccessor接口:提供了可以访问命名属性(bean属性或对象中的字段)的接口。提供了对JavaBean属性的读取和写入操作。

图 PropertyAccessor 接口的方法

2.3 BeanWrapperImpl

为标准bean属性的访问实现的方法。还支持类型转化及自定义属性编辑等功能。

图 BeanWrapperImpl UML

BeanWrapper 继承了ConfigurablePropertyAccessor 接口,并增加了getWrappedInstance()获取将目标对象包装后的对象等方法。

AbstractNestablePropertyAccessor 主要用于支持嵌套属性的访问和类型转换。

2.3.1 AbstractNestablePropertyAccessor

图 AbstractPropertyAccessor 中的setPropertyValues方法部分截图

通过遍历属性值的List集合,来为每个属性值进行绑定。

图 AbstractNestablePropertyAccessor 中的setPropertyValue 方法

AbstractNestrablePropertyAccessor 中实现了父类抽象类的抽象方法setPropertyValue,来完成对单个属性值的绑定。

PropertyTokenHolder 是AbstractNestrablePropertyAccessor 的一个内部类,主要作用是解析和处理嵌套属性的名称,并将它们转换成可访问和操作的形式。

actualName

属性的实际名称。可能包含了方括号和引号等元素格式。

canonicalName

属性的规范名称。会去除属性名称中的方括号和引号,并将属性名称转换为规范的形式。例如,person[‘address’][‘city’]转换为address.city。

keys:String[]

保存了属性的键列表,例如上面的属性保存为[“address”,“city”]

表 PropertyTokenHolder 的字段

图 PropertyTokenHolder 代码

2.4 processLocalProperty方法完成属性类型转换及绑定

图 processLocalProperty方法中的关键代码

2.4.1 PropertyHandler

是一个抽象类,用于处理属性的读取和写入操作。定义了获取/设置属性值及检查属性是否可读可写的方法。其默认实现是BeanPropertyHandler, 针对JavaBean属性,使用JavaBean规范来操作属性。如果不是通过getter和setter方法访问的,则使用LocalPropertyHandler。

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

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

相关文章

【k近邻】Kd树构造与最近邻搜索示例

【k近邻】 K-Nearest Neighbors算法原理及流程 【k近邻】 K-Nearest Neighbors算法距离度量选择与数据维度归一化 【k近邻】 K-Nearest Neighbors算法k值的选择 【k近邻】 Kd树的构造与最近邻搜索算法 【k近邻】 Kd树构造与最近邻搜索示例 近邻法的实现需要考虑如何快速搜索个最…

Python习题详解

练习&#xff1a; 1&#xff0c;计算100以内奇数的和 #计算100以内所有奇数的和 sum 0 # n 1 # while n < 100: # # sum sum n # sum n # # n n 2 # n 2 # print(sum) n 99 #求偶数时n 100 while n > 0: sum n # n n - 2 n - 2 print(sum)2&#xff0c;打印…

缓存篇—缓存穿透

当发生缓存雪崩或击穿时&#xff0c;数据库中还是保存了应用要访问的数据&#xff0c;一旦缓存恢复相对应的数据&#xff0c;就可以减轻数据库的压力&#xff0c;而缓存穿透就不一样了。 当用户访问的数据&#xff0c;既不在缓存中&#xff0c;也不在数据库中&#xff0c;导致…

论文阅读《Sylph: A Hypernetwork Framework for Incremental Few-shot Object Detection》

论文地址&#xff1a;https://arxiv.org/abs/2203.13903 代码地址&#xff1a;https://github.com/facebookresearch/sylph-few-shot-detection 目录 1、存在的问题2、算法简介3、算法细节3.1、基础检测器3.2、小样本超网络3.2.1、支持集特征提取3.2.2、代码预测3.2.3、代码聚合…

【Vulkan Tutorials 01】【环境搭建】三角形例子

Development Environment&#xff08;开发环境&#xff09; 1. 安装Vulkan SDK 官网 2. 安装cmake和minGW 2.1 cmake 官网 双击可执行文件&#xff0c;然后直接安装&#xff0c;注意环境变量选择设置&#xff0c;否则需要自己操作。 2.2 minGW 官网 下载如下图所示&am…

【Pytorch深度学习开发实践学习】B站刘二大人课程笔记整理lecture08数据集导入和构建

lecture08数据集导入和构建 课程网址 Pytorch深度学习实践 部分课件内容&#xff1a; import torch from torch.utils.data import Dataset, DataLoader import numpy as npclass DiabetesDataset(Dataset):def __init__(self):xy np.loadtxt(diabetes.csv.gz, delimiter,, …

Sora:打开视频创作新纪元的魔法钥匙

随着人工智能技术的飞速发展&#xff0c;AI视频模型已成为科技领域的新热点。而在这个浪潮中&#xff0c;OpenAI推出的首个AI视频模型Sora&#xff0c;以其卓越的性能和前瞻性的技术&#xff0c;引领着AI视频领域的创新发展。让我们将一起探讨Sora的技术特点、应用场景以及对未…

vulnhub靶场之Deathnote

一.环境搭建 1.靶场描述 Level - easy Description : dont waste too much time thinking outside the box . It is a Straight forward box . This works better with VirtualBox rather than VMware 2.靶场下载 https://www.vulnhub.com/entry/deathnote-1,739/ 3.启动环…

【linux】shell命令 | Linux权限

目录 1. shell命令以及运行原理 2. Linux权限的概念 3. Linux权限管理 3.1 文件访问者的分类 3.2 文件类型和访问权限 3.3 文件权限值的表示方法 3.4 文件访问权限的相关设置方法 4. file指令 5. 目录的权限 6. 粘滞位 7. 关于权限的总结 1. shell命令以及运行原理 …

05.STLvector、list、stack、queue

STL标准模板库 standard template library STL将原来常用的容器和操作进行封装&#xff0c;增加了C的编码效率 容器 string #include vector #include list #include stack #include queue #include set #include map #include 迭代器 容器和算法之间的粘合剂&#xff0…

js 多对象去重(多属性去重)

需求中发现后端可能没有处理重复数据&#xff0c;这个时候前段可以直接解决。 在 JavaScript 中&#xff0c;可以使用 Set 数据结构来进行多对象的去重。Set 是 ES6 新引入的集合类型&#xff0c;其特点是元素不会重复且无序。 下面是一个示例代码&#xff0c;展示如何通过 S…

Nginx----高性能的WEB服务端

一、Nginx介绍 1、什么是Nginx Nginx Nginx是一个高性能的HTTP和反向代理服务器。是一款轻量级的高性能的web服务器/反向代理服务器/电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&#xff0c;单台物理服务器可支持30 000&#xff5e;50 000个并发请求。 一款高性能…

一分钟学会MobaXterm当Linux客户端使用

一、介绍 MobaXterm是一款功能强大的远程计算机管理工具&#xff0c;它集成了各种网络工具和远程连接协议&#xff0c;可以帮助用户在Windows系统上轻松管理远程计算机。MobaXterm支持SSH、Telnet、RDP、VNC等多种远程连接协议&#xff0c;同时还集成了X11服务器&#xff0c;可…

启动node服务报错Error: listen EACCES: permission denied 0.0.0.0:5000

启动node服务报错&#xff1a; 解决方案&#xff1a; 将监听端口改成3000或者其他 修改后结果&#xff1a; 参考原文&#xff1a; Error: listen EACCES: permission denied_error when starting dev server: error: listen eacc-CSDN博客

并发编程入门指南

文章目录 并发编程进程和线程的区别并发和并行的区别创建线程的方式线程之间的状态&#xff0c;状态之间的转换新建三个线程&#xff0c;如何保证按顺序执行wait方法和sleep的区别如何停止一个正在运行的线程synchronized关键字底层原理Monitor属于重量级锁&#xff0c;了解过锁…

外贸邮件群发软件有效果吗?邮件群发平台?

外贸邮件群发软件怎么选&#xff1f;国外邮箱群发平台如何选择&#xff1f; 外贸业务已成为许多企业发展的重要方向。在这个过程中&#xff0c;各种工具和技术层出不穷&#xff0c;其中外贸邮件群发软件因其能够高效、批量地发送邮件而备受关注。那么&#xff0c;外贸邮件群发…

API接口测试工具的使用指南

API接口测试是确保软件系统正常运作的关键步骤。API接口测试工具可以帮助开发人员和测试人员验证API的功能、性能和安全性。本文将介绍API接口测试工具的基本使用方法&#xff0c;以便有效地进行接口测试。 1. 选择合适的API测试工具 在开始API接口测试之前&#xff0c;首先需要…

STM32F10X(Cortex-M3)系统定时器寄存器笔记和系统定时器精准延时函数

Cortex-M3系统定时器寄存器笔记和系统定时器精准延时函数 简介系统定时器寄存器STK_CTRLSTK_LOADSTK_VALSTK_CALIB STM32F10X(Cortex-M3)精准延时函数 简介 在STM32F10X(Cortex-M3)除了通用定时器和看门狗定时器外&#xff0c;还有一个系统定时器(SysTick) 拿STM32F103C8T6来说…

vue如何动态加载显示本地图片资源

在实际开发中&#xff0c;根据某一个变量动态展示图片的情况有很多。实现方法分打包构建工具的差异而不同。 1、webpack的项目 require引入图片资源 2、vite的项目 new URL(url,base).href 疑问解答&#xff1a;为什么vite项目不可以用require&#xff1f; 原因在于&#xf…

vue项目设置的端口号运行后会自动加一问题解决

vue项目设置的端口号运行后会自动加一问题解决 主要原因是之前运行项目后没有完全的关闭服务&#xff0c;导致再次运行项目端口号被占用&#xff0c;自动加一&#xff01; 问题解决 打开任务管理器&#xff0c;在进程中找到node相关进程&#xff0c;右键结束任务