SpringMvc的参数绑定原理

前言

我们在使用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;
                    }
                });
           }
       };
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值