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);
}
});
}
}