SpringBoot核心技术-Web开发-自定义对象参数解析原理

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的结构如下:

image.png

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。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值