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测试
测试的时候就会在全部转换器中找到我们定制的哪一个
分类:
java
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了