接着前一篇博客:SpringMVC:请求参数解析原理
测试
@Data
public class Person {
private String userName;
private Integer age;
private Date birth;
private Pet pet;
}
@Data
public class Pet {
private String name;
private String age;
}
index.html
<form action="save" method="post">
姓名: <input name="userName" value="admin"/> <br/>
年龄: <input name="age" value="12"/> <br/>
生日: <input name="birth" value="2021/01/01"/> <br/>
宠物姓名:<input name="pet.name" value="cat"/><br/>
宠物年龄:<input name="pet.age" value="3"/>
<input type="submit" value="保存">
</form>
controller:
@PostMapping("save")
@ResponseBody
public Person save(Person person){
return person;
}
源码剖析
上一篇博客SpringMVC:请求参数解析原理,已经详细分析了在哪一步决定使用哪一个参数解析器。
经过断点分析,最后是由ServletModelAttributeMethodProcessor所处理的,而supportsParameter方法是由它的父类ModelAttributeMethodProcessor实现的
/**
* Returns {@code true} if the parameter is annotated with
* {@link ModelAttribute} or, if in default resolution mode, for any
* method parameter that is not a simple type.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
//是否标注了ModelAttribute注解或者(不是必须的? && 不是简单类型)
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
BeanUtils.isSimpleProperty方法:
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
isSimpleValueType方法(判断是否是如下几个类型),很明显都不是,返回false
type.isArray(),是否是数组?也不是,返回false,所以整个isSimpleProperty返回false
所以supportsParameter方法最终返回true,最终由ServletModelAttributeMethodProcessor来解析该自定义参数类型。
下面开始执行ModelAttributeMethodProcessor的handleReturnValue方法:
![image-20210406223916704](https://gitee.com/wj204811/wj204811/raw/master/img/20210406223916.png)
通过打断点,得出createAttribute创建了一个空对象,那么空对象如何绑定属性的,应该在该方法后面执行的,也是我们需要关注的,代码如下:
WebDataBinder:创建了一个web数据绑定器,通过web数据绑定器就可以将请求参数的值封装到实体类中
if (bindingResult == null) {
//
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
//绑定请求参数,重要!!!
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
binder的属性:
![image-20210406224457574](https://gitee.com/wj204811/wj204811/raw/master/img/20210406224457.png)
bindRequestParameters方法
bind方法:
public void bind(ServletRequest request) {
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/")) {
HttpServletRequest httpServletRequest = WebUtils.getNativeRequest(request, HttpServletRequest.class);
if (httpServletRequest != null) {
StandardServletPartUtils.bindParts(httpServletRequest, mpvs, isBindEmptyMultipartFiles());
}
}
addBindValues(mpvs, request);
doBind(mpvs);
}
MutablePropertyValues中封装了各种请求参数的key和value
doBind(mpvs):
applyPropertyValues方法:
setPropertyValues方法:
![image-20210406225511910](https://gitee.com/wj204811/wj204811/raw/master/img/20210406225511.png)
![image-20210406225819775](https://gitee.com/wj204811/wj204811/raw/master/img/20210406225819.png)
![image-20210406225841351](https://gitee.com/wj204811/wj204811/raw/master/img/20210406225841.png)
processLocalProperty方法:
![image-20210406230033631](https://gitee.com/wj204811/wj204811/raw/master/img/20210406230033.png)
打断点,最终到convertIfNecessary方法:
![image-20210406230309288](https://gitee.com/wj204811/wj204811/raw/master/img/20210406230309.png)
这里跟请求参数解析器判断差不多。
canConvert方法:
@Override
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
getConverter方法:通过find方法找到对应的converter
![image-20210406230516310](https://gitee.com/wj204811/wj204811/raw/master/img/20210406230516.png)
find方法:
conversionService.convert(newValue, sourceTypeDesc, typeDescriptor)源码:
![image-20210406231319424](https://gitee.com/wj204811/wj204811/raw/master/img/20210406231319.png)
最后的convert方法很简单:
convert完成后,再将最后的值设置到实体类中。
总结
经过上述源码分析后,可总结出:
ServletModelAttributeMethodProcessor
这个参数解析器支持自定义参数绑定,它在底层使用了WebDataBinder
数据绑定器,而数据绑定器中有一个conversionService
,conversionService
中注册了很多converters
,converters
会帮助我们进行类型转换。