spring实体类(POJO)参数的赋值(form表单)原理
10、实体类(POJO)参数的赋值(form表单)原理
10.1、原理解析
测试用例
- 准备好两个实体类
public class Person {
private String name;
private Integer age;
private Pet pet;
} public class Pet {
private String name;
private Integer age;
}
html的form表单
注意这个 宠物Pet对象的name不能乱写 必须要和 person中定义的名称一样 才可以
<form action="/person" method="post">
<input type="text" name="name" value="水三丫">
<input type="text" name="age" value="18">
<input type="text" name="pet.name" value="阿猫">
<input type="text" name="pet.age" value="10">
<input type="submit" value="Person">
</form>
- Controller请求代码
@PostMapping("/person")
public Map person(Person person){
Map<String, Person> map = new HashMap<>();
map.put("person",person);
return map;
}
源码Debug
从DispatchServlet 的 doDispatch方法开始
DispatchServlet类中的
doDispatch方法
//RequestMappingHandlerAdapter这个处理器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
获取请求参数的处理器
HandlerMethodArgumentResolverComposite类中的
getArgumentResolver方法
//总共27个处理器
ServletModelAttributeMethodProcessor这个处理Pojo实体类的参数
创建实体类构造器和赋值
- 创造构造器
ModelAttributeMethodProcessor类中的
resolveArgument方法
// Create attribute instance
try {
attribute = createAttribute(name, parameter,
binderFactory, webRequest);
}
createAttribute方法 //反射获取空参构造器
Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
Constructor<?> ctor = BeanUtils.getResolvableConstructor(clazz);
Object attribute = constructAttribute(ctor, attributeName, parameter,
binderFactory, webRequest);

- 属性绑定和验证
ModelAttributeMethodProcessor类中的
resolveArgument方法
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute,
name);

- 获取请求的参数
ServletModelAttributeMethodProcessor类中订单
bindRequestParameters方法
servletBinder.bind(servletRequest);
ServletRequestDataBinder类中的
bind方法
MutablePropertyValues mpvs = new
ServletRequestParameterPropertyValues(request);
WebUtils方法类中
getParametersStartingWith方法
Enumeration<String> paramNames = request.getParameterNames();
Map<String, Object> params = new TreeMap<>();

准备开始数据绑定
ServletRequestDataBinder类中的
bind方法
doBind(mpvs);
WebDataBinder类中的
doBind方法
super.doBind(mpvs);
DataBinder类中的
doBind方法
applyPropertyValues(mpvs);
AbstractPropertyAccessor类中的
setPropertyValues方法
try {
setPropertyValue(pv);
}
第一步获取数据的绑定格式转换的处理器(因为浏览器以JSOn穿过了的都是字符串)需要格式转换
TypeConverterDelegate类中的
convertIfNecessary方法
try {
//传入的是请求的参数值 参数类型 需要转换的类型
return (T) conversionService.convert(newValue, sourceTypeDesc,
typeDescriptor);
}
GenericConversionService类中的
convert方法
GenericConverter converter = getConverter(sourceType, targetType);
getConverter方法
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
GenericConverter converter = this.converterCache.get(key);
需要124个类型转换中寻找这次需要的

需要的是:

获取的转换器

第二步转换数据
GenericConversionService类中的
convert方法
Object result = ConversionUtils.invokeConverter(converter, source,
sourceType,targetType);
第三步开始赋值
AbstractNestablePropertyAccessor类中的
processLocalProperty方法
ph.setValue(valueToApply);
获取set方法
BeanWrapperImpl类中的
setValue方法
Method writeMethod = (this.pd instanceof
GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor)
this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
开始反射赋值
BeanWrapperImpl类中的
setValue方法
writeMethod.invoke(getWrappedInstance(), value);
赋值完成

10.2、定制化属性转换器
编写自定义配置转换规则
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {//增加一个转换器
@Override
public Pet convert(String source) {//实现这个接口的方法
if(!StringUtils.isEmpty(source)){
Pet pet = new Pet();
String[] split = source.split(",");//以逗号为分隔符
pet.setName(split[0]);
pet.setAge(Integer.parseInt(split[1]));
return pet;
}
return null;
}
});
}
};
}
lambda表达式写法
@Bean
public WebMvcConfigurer webMvcConfigurer(){
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(String.class,Pet.class, source -> { if(!StringUtils.isEmpty(source)){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(Integer.parseInt(split[1]));
return pet;
}
return null;
});
}
};
}
测试用例
html页面form表单代码
<form action="/person1" method="post">
<input type="text" name="name" value="水三丫">
<input type="text" name="age" value="18">
<input type="text" name="pet" value="阿猫,10">
<input type="submit" value="Person1">
</form>
Debug测试
测试的时候就会在全部转换器中找到我们定制的哪一个

spring实体类(POJO)参数的赋值(form表单)原理的更多相关文章
- js 取值&赋值-form表单
form表单元素介绍 CreateTime--2016年9月22日10:25:54 Author:Marydon <form> 表单元素. 表单中的元素: <input>表 ...
- 1113 form表单与css选择器
目录 1.form表单 form元素 特点 参数 form元素内的控件 1.input的使用 2.select标签 3.textarea元素 4.autofocus属性 2.CSS 1.基础语法 cs ...
- Java如何实现form表单提交的数据自动对应实体类(源码)
原文出自:https://blog.csdn.net/seesun2012 原生Java+JQuery form表单serializeArray提交自动对应java实体,这是一个实际的例子: html ...
- @NamedEntityGraphs --JPA按实体类对象参数中的字段排序问题得解决方法
JPA按实体类对象参数中的字段排序问题得解决方法@Entity @Table(name="complaints") @NamedEntityGraphs({ @NamedEntit ...
- c# 实体类怎么给LIST赋值,table转LIST
/// <summary> /// 缓存客服集合信息 /// </summary> public class model { /// <summary> /// 客 ...
- 实体类相同属性间赋值与如何判断实体类中是否所有的字段都为null
1,实体类相同属性间赋值 /// <summary> /// 将实体2的值动态赋值给实体1(名称一样的属性进行赋值) /// </summary> /// <param ...
- SpringMVC中使用bean来接收form表单提交的参数时的注意点
这是前辈们对于SpringMVC接收表单数据记录下来的总结经验: SpringMVC接收页面表单参数 springmvc请求参数获取的几种方法 下面是我自己在使用时发现的,前辈们没有记录的细节和注意点 ...
- JsonResponse类的使用、form表单上传文件补充、CBV和FBV、HTML的模板语法之传值与过滤器
昨日内容回顾 Django请求生命周期 # 1.浏览器发起请求 到达Django的socket服务端(web服务网关接口) 01 wsgiref 02 uwsgi + nginx 03 WSGI协议 ...
- Form 表单提交参数
今天因为要额外提交参数数组性的参数给form传到后台而苦恼了半天,结果发现,只需要在form表单对应的字段html空间中定义name = 后台参数名 的属性就ok了. 后台本来是只有模型参数的,但是后 ...
随机推荐
- 791. Custom Sort String - LeetCode
Question 791. Custom Sort String Solution 题目大意:给你字符的顺序,让你排序另一个字符串. 思路: 输入参数如下: S = "cba" T ...
- k8s docker 中部署think php 并搭建php websocket
不得不说php 对云原生有点不够友好,之前用java .net打包docker镜像 一下就ok了,php倒腾了好久才算部署成功. 场景:使用阿里云ack(k8s) 部署采用thinkPHP框架的php ...
- async用法
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...
- HDFS 细粒度锁优化,FusionInsight MRS有妙招
摘要:华为云FusionInsight MRS通过FGL对HDFS NameNode锁机制进行优化,有效提升了NameNode的读写吞吐量,从而能够支持更多数据,更多业务请求访问,从而更好的支撑政企客 ...
- 基于C++11的数据库连接池实现
0.注意 该篇文章为了让大家尽快看到效果,代码放置比较靠前,看代码前务必看下第4部分的基础知识. 1.数据库连接池 1.1 是什么? 数据库连接池负责分配.管理和释放数据库连接,属于池化机制的一种,类 ...
- DirectX11 With Windows SDK--40 抗锯齿:FXAA
前言 在默认的情况下渲染,会看到物体的边缘会有强烈的锯齿感,究其原因在于采样不足.但是,尝试提升采样的SSAA会增大渲染的负担:而硬件MSAA与延迟渲染又不能协同工作.为此我们可以考虑使用后处理的方式 ...
- python爬虫之JS逆向
Python爬虫之JS逆向案例 由于在爬取数据时,遇到请求头限制属性为动态生成,现将解决方式整理如下: JS逆向有两种思路: 一种是整理出js文件在Python中直接使用execjs调用js文件(可见 ...
- 基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传
在基于SqlSugar的开发框架的服务层中处理文件上传的时候,我们一般有两种处理方式,一种是常规的把文件存储在本地文件系统中,一种是通过FTP方式存储到指定的FTP服务器上.这种处理应该由程序进行配置 ...
- 谷歌浏览器Chrome官方下载地址
经常看到朋友的电脑上安装是魔改的谷歌浏览器.这里将谷歌浏览器官方的下载地址放在这里.有需要的朋友可以自己去下载. 下载地址 Chrome最新稳定在线安装版:https://www.google.cn/ ...
- 重学ES系列之过滤数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...