SpringMVC:自定义参数绑定原理

接着前一篇博客:SpringMVC:请求参数解析原理

测试

@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}
@Data
public class Pet {
    private String name;
    private String age;
}

index.html

<form action="save" method="post">
    姓名: <input name="userName" value="admin"/> <br/>
    年龄: <input name="age" value="12"/> <br/>
    生日: <input name="birth" value="2021/01/01"/> <br/>
    宠物姓名:<input name="pet.name" value="cat"/><br/>
    宠物年龄:<input name="pet.age" value="3"/>
    <input type="submit" value="保存">
</form>

controller:

@PostMapping("save")
@ResponseBody
public Person save(Person person){
    return person;
}

image-20210406220523963

源码剖析

上一篇博客SpringMVC:请求参数解析原理,已经详细分析了在哪一步决定使用哪一个参数解析器。

image-20210406221804284

经过断点分析,最后是由ServletModelAttributeMethodProcessor所处理的,而supportsParameter方法是由它的父类ModelAttributeMethodProcessor实现的

	/**
	 * Returns {@code true} if the parameter is annotated with
	 * {@link ModelAttribute} or, if in default resolution mode, for any
	 * method parameter that is not a simple type.
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
        //是否标注了ModelAttribute注解或者(不是必须的? && 不是简单类型)
		return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
				(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
	}

BeanUtils.isSimpleProperty方法:

	public static boolean isSimpleProperty(Class<?> type) {
		Assert.notNull(type, "'type' must not be null");
		return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
	}

isSimpleValueType方法(判断是否是如下几个类型),很明显都不是,返回false

image-20210406222804075

type.isArray(),是否是数组?也不是,返回false,所以整个isSimpleProperty返回false

所以supportsParameter方法最终返回true,最终由ServletModelAttributeMethodProcessor来解析该自定义参数类型。

下面开始执行ModelAttributeMethodProcessor的handleReturnValue方法:

image-20210406223916704

通过打断点,得出createAttribute创建了一个空对象,那么空对象如何绑定属性的,应该在该方法后面执行的,也是我们需要关注的,代码如下:

WebDataBinder:创建了一个web数据绑定器,通过web数据绑定器就可以将请求参数的值封装到实体类中

		if (bindingResult == null) {
            //
			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;

binder的属性:

image-20210406224457574

bindRequestParameters方法

image-20210406224635948

bind方法:

	public void bind(ServletRequest request) {
		MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
		MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
		if (multipartRequest != null) {
			bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
		}
		else if (StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/")) {
			HttpServletRequest httpServletRequest = WebUtils.getNativeRequest(request, HttpServletRequest.class);
			if (httpServletRequest != null) {
				StandardServletPartUtils.bindParts(httpServletRequest, mpvs, isBindEmptyMultipartFiles());
			}
		}
		addBindValues(mpvs, request);
		doBind(mpvs);
	}

MutablePropertyValues中封装了各种请求参数的key和value

image-20210406224901183

doBind(mpvs):

image-20210406225112198

image-20210406225128988

applyPropertyValues方法:

image-20210406225253242

setPropertyValues方法:

image-20210406225511910 image-20210406225819775 image-20210406225841351

processLocalProperty方法:

image-20210406230033631

打断点,最终到convertIfNecessary方法:

image-20210406230309288

这里跟请求参数解析器判断差不多。

canConvert方法:

	@Override
	public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
		Assert.notNull(targetType, "Target type to convert to cannot be null");
		if (sourceType == null) {
			return true;
		}
		GenericConverter converter = getConverter(sourceType, targetType);
		return (converter != null);
	}

getConverter方法:通过find方法找到对应的converter

image-20210406230516310

find方法:

image-20210406230955532

conversionService.convert(newValue, sourceTypeDesc, typeDescriptor)源码:

image-20210406231319424

image-20210406231648555

最后的convert方法很简单:

image-20210406231721724

convert完成后,再将最后的值设置到实体类中。

总结

经过上述源码分析后,可总结出:

ServletModelAttributeMethodProcessor这个参数解析器支持自定义参数绑定,它在底层使用了WebDataBinder数据绑定器,而数据绑定器中有一个conversionServiceconversionService中注册了很多convertersconverters会帮助我们进行类型转换。

原文地址:https://www.cnblogs.com/wwjj4811/p/14624599.html