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了. 后台本来是只有模型参数的,但是后 ...
随机推荐
- linux篇-新建svn仓库
1昨天需要在服务器上新建一个仓库,解决方法是把已有的仓库拷贝出来,库删除在放进去 2然后今天想看看有没有命令的方法 find / -name project 首先查看一下项目的位置 3创建仓库 svn ...
- 【Azure Developer】App Service + PubSub +JS 实现多人版黑客帝国文字流效果图
需要描述 1)实现黑客帝国文字流效果图,JS功能 2)部署在云中,让大家都可以访问,App Service实现 3)大家都能发送消息,并显示在文字流中,PubSub(websocket)实现 终极效果 ...
- 背包,子集和以及 (max, +) 卷积在特殊情形下的求法
背包,子集和以及 (max, +) 卷积在特殊情形下的求法 子集和 1:总重量不太大 有 \(n\) 个物品,每个物品重量为 \(w_i\),且 \(\sum\limits_{i} w_i=C\).你 ...
- fpm工具安装
概述 最近在对机房的编译环境做整理,过程曲折而痛苦,记录一下. 之前的一个老项目,在打包的时候用到了一个叫做fpm的工具. 编译环境涉及centos6和centos7,在新的编译环境的过程中,如何安装 ...
- Windows系统开启显示文件名后缀
更新记录 2022年4月16日:本文迁移自Panda666原博客,原发布时间:2021年8月26日. 通常Windows系统根据文件名称的后缀来确定文件的类型.经常让朋友出现软件方面的问题,让其修改一 ...
- ASP.NET Core 应用配置指定地址和端口
更新记录 本文迁移自Panda666原博客,原发布时间:2021年5月10日. 几种方式 ASP.NET Core 应用配置指定地址和端口支持以下几种主要方式: 1.在命令行模式启动应用时设置 --u ...
- 写个js获取2019博客之星投票活动的名次与投票数
获取投票数 // app.jsvar request = require('request');var cheerio = require('cheerio');request('http://m23 ...
- SAP APO-需求计划
需求计划可以对市场中的产品进行预测. 需求计划过程的输出就是需求计划,它考虑了影响需求的所有因素. 需求计划流程定义了需求计划周期中的活动. 由于需求计划过程以循环的形式进行,因此可以重复某些活动. ...
- SAP IDOC
物料主数据 供应商主数据 价格档案 采购订单 采购计划协议 srm发货单 物料凭证 发票校验 发票校验过账或删除信息返回 CALL FUNCTION 'BAPI_INCOMINGINVOICE_PAR ...
- QT多线程的简单使用,主线程发一份数据,子线程收两份数据
先看效果图,示例发送数据"Hello World" 主线程:0x16f54aeda20,另两个子线程分别是0x4f1baff690.0x4f1baff6a0 因为在子线程中加了20 ...