Spring源码学习之Web数据绑定器WebDataBinder

发布时间 2023-11-30 13:24:25作者: 锅巴编程

WebDataBinder

1.描述

特殊的数据绑定器用于从web请求参数到JavaBean对象的数据绑定。专为web环境,但不依赖于Servlet API;作为更具体的DataBinder变体的基类,例如ServletRequestDataBinder。

2.注意:数据绑定会暴露对象图中不打算被外部客户端访问或修改的部分,从而导致安全问题。因此,数据绑定的设计和使用应该仔细考虑安全性。

3.包括对字段标记的支持,它解决了HTML复选框和选择选项的一个常见问题:检测字段是表单的一部分,但因为它是空的而没有生成请求参数。字段标记允许检测该状态并相应地重置相应的bean属性。对于不存在的参数,默认值可以为字段指定一个值,而不是空值。


public class WebDataBinder extends DataBinder {

//字段标记参数开头的默认前缀,后面跟着字段名
	public static final String DEFAULT_FIELD_MARKER_PREFIX = "_";//默认字段标记前缀
	public static final String DEFAULT_FIELD_DEFAULT_PREFIX = "!";//默认字段默认前缀
	@Nullable//该注解代表修饰属性可以为空
	
	//此类的有参构造  参数代表要绑定的目标对象
	public WebDataBinder(@Nullable Object target) {
		super(target);
	}
	
	//重载的有参构造  参数是绑定目标对象和目标对象名称
	public WebDataBinder(@Nullable Object target, String objectName) {
		super(target, objectName);
	}
	
	//设置字段标记前缀方法
	public void setFieldMarkerPrefix(@Nullable String fieldMarkerPrefix) {
		this.fieldMarkerPrefix = fieldMarkerPrefix;
	}

	//获取字段标记前缀
	@Nullable  //可以为空用于声明注释元素在某些情况下可以为空
	public String getFieldMarkerPrefix() {
		return this.fieldMarkerPrefix;
	}
	
	//获取字段默认前缀
	public void setFieldDefaultPrefix(@Nullable String fieldDefaultPrefix) {
		this.fieldDefaultPrefix = fieldDefaultPrefix;
	}
	
	
	//设置“绑定空多部分文件”
	public void setBindEmptyMultipartFiles(boolean bindEmptyMultipartFiles) {
		this.bindEmptyMultipartFiles = bindEmptyMultipartFiles;
	}
	
	//绑定是空的多部分文件
	public boolean isBindEmptyMultipartFiles() {
		return this.bindEmptyMultipartFiles;
	}
	
	//这个重写的方法 在委托给超类绑定过程之前 执行字段默认值和标记检查
	//MutablePropertyValues 可变属性值
	@Override
	protected void doBind(MutablePropertyValues mpvs) {
		checkFieldDefaults(mpvs);
		checkFieldMarkers(mpvs);
		adaptEmptyArrayIndices(mpvs);
		super.doBind(mpvs);
	}
	
	
	//检查字段默认值的给定属性值,即以字段默认前缀开头的字段
	protected void checkFieldDefaults(MutablePropertyValues mpvs) {
		String fieldDefaultPrefix = getFieldDefaultPrefix();
		if (fieldDefaultPrefix != null) {
			PropertyValue[] pvArray = mpvs.getPropertyValues();
			for (PropertyValue pv : pvArray) {
				if (pv.getName().startsWith(fieldDefaultPrefix)) {
					String field = pv.getName().substring(fieldDefaultPrefix.length());
					if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
						mpvs.add(field, pv.getValue());
					}
					mpvs.removePropertyValue(pv);
				}
			}
		}
	}
	
	//检查字段标记的给定属性值,即以字段标记前缀开头的字段
	protected void checkFieldMarkers(MutablePropertyValues mpvs) {
		String fieldMarkerPrefix = getFieldMarkerPrefix();
		if (fieldMarkerPrefix != null) {
			PropertyValue[] pvArray = mpvs.getPropertyValues();
			for (PropertyValue pv : pvArray) {
				if (pv.getName().startsWith(fieldMarkerPrefix)) {
					String field = pv.getName().substring(fieldMarkerPrefix.length());
					if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
						Class<?> fieldType = getPropertyAccessor().getPropertyType(field);
						mpvs.add(field, getEmptyValue(field, fieldType));
					}
					mpvs.removePropertyValue(pv);
				}
			}
		}
	}
	
	//检查名称以{@code "[]"}结尾的属性值。这被一些客户端用于没有显式索引值的数组语法。如果找到了这样的值,请去掉括号,以适应数据绑定目的所期望的表示相同值的方式。
	protected void adaptEmptyArrayIndices(MutablePropertyValues mpvs) {
		for (PropertyValue pv : mpvs.getPropertyValues()) {
			String name = pv.getName();
			if (name.endsWith("[]")) {
				String field = name.substring(0, name.length() - 2);
				if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
					mpvs.add(field, pv.getValue());
				}
				mpvs.removePropertyValue(pv);
			}
		}
	}
	
	//获取空值  确定指定字段的空值
	@Nullable
	protected Object getEmptyValue(String field, @Nullable Class<?> fieldType) {
		return (fieldType != null ? getEmptyValue(fieldType) : null);
	}
	
	//获取空值  参数:字段类型
	@Nullable
	public Object getEmptyValue(Class<?> fieldType) {
		try {
			if (boolean.class == fieldType || Boolean.class == fieldType) {
				// Special handling of boolean property.
				return Boolean.FALSE;
			}
			else if (fieldType.isArray()) {
				// Special handling of array property.
				return Array.newInstance(fieldType.getComponentType(), 0);
			}
			else if (Collection.class.isAssignableFrom(fieldType)) {
				return CollectionFactory.createCollection(fieldType, 0);
			}
			else if (Map.class.isAssignableFrom(fieldType)) {
				return CollectionFactory.createMap(fieldType, 0);
			}
		}
		catch (IllegalArgumentException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to create default value - falling back to null: " + ex.getMessage());
			}
		}
		// Default value: null.
		return null;
	}
	
	//绑定给定请求中包含的所有多部分文件(如果有的话)(对于多部分请求)。被子类调用
	protected void bindMultipart(Map<String, List<MultipartFile>> multipartFiles, MutablePropertyValues mpvs) {
		multipartFiles.forEach((key, values) -> {
			if (values.size() == 1) {
				MultipartFile value = values.get(0);
				if (isBindEmptyMultipartFiles() || !value.isEmpty()) {
					mpvs.add(key, value);
				}
			}
			else {
				mpvs.add(key, values);
			}
		});
	}
}