这里的自定义参数解析指的是POJO类型的封装过程。
首页写一表单:
且声明两个POJO类(目标参数):
且Pet和Person是级联关系。
(@Data是lombok提供的补充POJO类getter、setter、toString方法实现的注释)
POJO类的封装过程是由ServletModelAttributeMethodProcessor执行的。
只有第二个ServletModelAttributeMethodProcessor才是符合的
两者不一样,后者不需要@ModelAttribute注释:stepInto这个supportsParameter方法查看,
要么1有这个annoation(@ModelAttribute)要么2不需要注释(annotationNotRequired)而且不是简单参数(isSimpleProperty)。
那么问题就来了,怎么判断是否为简单参数?通过isSimpleProperty方法判断:
首先参数类型type非null。(type就是前文的parameter.getParameterType()得到的值)其次还需要①type满足isSimpleValueType方法or②type是数组&type的component类型也满足isSimpleValueType方法。其实应该加上():
先evaluate各部分的值:
可见返回false。
也可以进入isSimpleValueType方法,看那些类型是符合SimpleValue要求的:
因此可以知道supportsParameter方法返回值为true:
(一)接着解析参数,进入解析参数的核心方法resolveArgument:
![](https://i-blog.csdnimg.cn/blog_migrate/3954ecaa180d63142a0a8abe75591d46.png)
先判断是否有加ModelAttribute注解,然后得到一个新的ModelAttribute对象。这里我们明显是没有加的,所以ann对象为null。(但是如果有注解的话,mavContainer就会将POJO参数的名字跟这个modelAttribute对象进行绑定。)后面进行//create attribute instance(属性实例的创建):
![](https://i-blog.csdnimg.cn/blog_migrate/7187bfd2aaed6fbc7563c853ba732b1d.png)
进入createAttribute方法:
![](https://i-blog.csdnimg.cn/blog_migrate/0e39ab512aa03723e3022c3076159280.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5a95087b690b457eeca6e6d1f26495c4.png)
可见内层的createAttribute方法就是根据parameter的信息——>>POJO类的构造器clazz(attributeName是'person') 。
然后建立了attribute对象。返回的attribute为:
attribute是Person类型的,但是value全为null,还未给各个参数绑定(binding)对应的值。
(oops...突然发现忘记在Person类中级联添加Pet为级联对象属性了)
(二)此时又不得不提到我们的原生请求(携带所有请求信息,待处理)了,依旧是在ModelAttributeMethodProcessor类中,进入binding(值)阶段:
(注:binding阶段不仅1bind绑定还会进行2validate验证)
![](https://i-blog.csdnimg.cn/blog_migrate/ad94180b32abd491cb03fdd2f841f5fd.png)
最核心的部分就是createBinder方法:
查看binder是何方神圣?
WebDataBinder(Web数据绑定器)将请求参数的值绑定到指定的JavaBean(POJO类对象)中。
具体是如何实现的呢?binder的
①target包含了我们刚刚创建的空atrribute(请求参数)的信息,而且binder含有一个convertService(数据“转换”服务)
②可以利用其中的cjinonverters将请求数据转换成指定的数据类型,再次封装到JavaBean对象中:
然后经过以下方法后binder中原值为null的target(目标注入值对象)——Person已经被赋值了:
注:此处省略bindRequestParameters方法的解析(可能后面会填坑)。
这个方法主要就是如何给请求参数绑定值的各种转换细节:
【附】springMVC做了很多准备工作:
比如自动配置中的converters。 下面的ConfigurableWebBindingInitializer就是一个例子:
最后,附上resolveArgument解析参数的核心方法完整源码:
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = (ModelAttribute)parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
} else {
try {
attribute = this.createAttribute(name, parameter, binderFactory, webRequest);
} catch (BindException var10) {
if (this.isBindExceptionRequired(parameter)) {
throw var10;
}
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
} else {
attribute = var10.getTarget();
}
bindingResult = var10.getBindingResult();
}
}
if (bindingResult == null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
this.bindRequestParameters(binder, webRequest);
}
this.validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}