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了. 后台本来是只有模型参数的,但是后 ...
随机推荐
- TinyMCE简介
TinyMCE是一款开源.易用.UI时新的富文本编辑器. 插件丰富,自带插件基本满足要求 可扩展性强,可自定义功能 界面好看,符合现代审美 提供经典.内联.沉浸无干扰三种模式 官网:https://w ...
- 148. Sort List - LeetCode
Solution 148. Sort List Question 题目大意:对链表进行排序 思路:链表转为数组,数组用二分法排序 Java实现: public ListNode sortList(Li ...
- v86.01 鸿蒙内核源码分析 (静态分配篇) | 很简单的一位小朋友 | 百篇博客分析 OpenHarmony 源码
本篇关键词:池头.池体.节头.节块 内存管理相关篇为: v31.02 鸿蒙内核源码分析(内存规则) | 内存管理到底在管什么 v32.04 鸿蒙内核源码分析(物理内存) | 真实的可不一定精彩 v33 ...
- ML第5周学习小结
本周收获 总结一下本周学习内容: 1.学习了<深入浅出Pandas>的第五章:Pandas高级操作的两个内容 数据迭代 函数应用 我的博客链接: pandas:数据迭代.函数应用 2.&l ...
- 看Spring源码不得不会的@Enable模块驱动实现原理讲解
这篇文章我想和你聊一聊 spring的@Enable模块驱动的实现原理. 在我们平时使用spring的过程中,如果想要加个定时任务的功能,那么就需要加注解@EnableScheduling,如果想使用 ...
- swap函数模板
在许多应用程序中,都有交换相同类型的两个变量内容的需要.例如,在对整数数组进行排序时,将需要一个函数来交换两个变量的值,如下所示: void swap(int &a, int &b) ...
- ElasticSearch6.4.2
做一个简单的API记录 1.依赖为6.4.2 比较老的版本 2.指定ES集群,可接多个Put(); Setting setting=Setting.builder().put("clust ...
- Eureka高可用集群搭建
就是搭建Eureka的集群. 每个Eureka Server需要相互注册,确保数据一致. 我这里准备两个Eureka Server 他两的POM文件配置是一样的 <dependencies&g ...
- 使用 DartPad 制作代码实践教程
DartPad 是一个开源的.在浏览器中体验和运行 Dart 编程语言的线上编辑器,目标是为了帮助开发者更好地了解 Dart 编程语言以及 Flutter 应用开发. DartPad 项目起始于 20 ...
- ExtJS 布局-Absolute布局(Absolute layout)
更新记录: 2022年5月31日 发布本篇 1.说明 使用xy配置项设置子组件在父容器中绝对位置,本质是将子组件的CSS的position设置为absolute,然后使用x和y配置项映射到CSS的to ...