zoukankan      html  css  js  c++  java
  • 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会帮助我们进行类型转换。

  • 相关阅读:
    jquery城市选择案例
    jquery点击回到页面顶部方法
    jquery动态创建表格
    myeclipse、eclipse中项目复制后(可能无法访问)注意事项 .
    Myeclipse如何整合tomcat
    Java学习路线图
    ftp
    用javascript实现简体和繁体字间的转换
    FTP远程命令集
    JDBC-ODBC桥乱码问题解决方案
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14624599.html
Copyright © 2011-2022 走看看