前言
我们在使用Springboot编写Controller时,我们的方法参数也可以实现对POJO类型的封装,这是如何实现的,下面,我们将通过自定义Pojo对象进行参数封装的案例,刨析自定义对象参数解析原理。
一、 案例的准备
1. 前端页面
<form action="savePerson" method="post">
<input type="text" name="userName" PLACEHOLDER="用户名"><br>
<input type="date" name="birth" placeholder="生日"><br>
<input type="text" name="age" placeholder="年龄"><br>
<input type="submit" value="提交">
</form>
2. 后台
@PostMapping("/savePerson")
public String savePerson(Person person){
return person.toString();
}
3. Bean对象
@ToString
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person {
private String userName;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
private Integer age;
}
二、原理解析
1. 判断那些解析器可以支持
- ServletModelAttributeMethodProcessor
- 该参数处理器支持
- 判断是否支持和是否时简单类型
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
//BeanUtils判断是否为简单数据类型
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));
}
2. 解析和封装POJO
- 核心方法
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();
}
else {
attribute = ex.getTarget();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
//创建数据解析器,将请求参数值绑定到JavaBean里面
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;
}
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
- web数据帮定器,用于将请求参数绑定到JavaBean里面。
- 它可以利用它里面的Convers(124个内置),将请求数据转换成指定的数据类型,在封装到JavaBean
- GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean – Integer)
byte – > file
- 绑定请求参数到JavaBean
在之后,我们也可以自定义,在webDataBinder当中放入自己的类型转换器!
自定义转换器
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//不删除;矩阵变量就会胜生效
urlPathHelper.setRemoveSemicolonContent(false); //不删除;
configurer.setUrlPathHelper(urlPathHelper);
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
String[] split = source.split(",");
Pet pet = new Pet();
pet.setName(split[0]);
pet.setWight(Integer.parseInt(split[1]));
return pet;
}
});
}
};