1、使用举例
<form action="/saveuser" method="post">
姓名:<input name="userName" value="zhangsan"/><br/>
年龄:<input name="age" value="18"/> <br/>
生日:<input name="birth" value="2019/12/10"/> <br/>
宠物姓名:<input name="pet.name" value="阿猫"/> <br/>
宠物年龄:<input name="pet.age" value="5"/> <br/>
<!--input name="pet" value="阿猫,3"/-->
<input type="submit" value="保存">
</form>
@PostMapping("/saveuser")
public Person saveuser(Person person) {
return person;
}
2、原理
ServletModelAttrtbuteMethodProcessor这个参数解析器支持。
判断是否支持解析:如果请求参数类型不是简单类型,ServletModelAttrtbuteMethodProcessor就可以支持解析
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
ServletModelAttrtbuteMethodProcessor的解析过程:
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
@Override
@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 = 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 {
// Create attribute instance
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
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;
}
1、attribute = mavContainer.getModel().get(name);创建了空的自定义类型的实例
2、WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder 是Web数据绑定器,将请求参数的值绑定到指定的JavaBean里面,webRequest是原生的Servlert请求。
WebDataBinde中有ConversionService,ConversionService中的converters有124个converter,converter将Http传输的文本转为自定义类型需要的数据类型。
- WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
- GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean -- Integer)
WebDataBinder的结构如下:
3、bindRequestParameters(binder, webRequest);这步执行完,自定义对象就会完成值绑定。
先获取原生request中的键值对,再用拿到的request中的属性值,利用converts转换成对应的数据类型,最后反射设置自定对象的所有属性值。
未来我们可以给WebDataBinder里面放自己的Converter;
3、自定义Converter
假设Person中的pet以下面这种方式赋值:
<input name="pet" value="阿猫,3"/>
//1、WebMvcConfigurer定制化SpringMVC的功能
@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) {
// 啊猫,3
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;
}
});
}
};
}
之后Converters会变成125个,多了个自定义的Converter。