一起学SF框架系列5.12-spring-beans-数据绑定dataBinding

数据绑定有助于将用户输入动态绑定到应用程序的域模型(或用于处理用户输入的任何对象),主要用于web层,但实际可用于任何层。Spring提供了DataBinder来做到这一点,并提供了Validator进行数据验证,两者组成了完整的数据输入数据处理机制。但数据绑定主要应用于 Spring 内部(如在SpringMVC中,http传入/返回参数如何绑定到具体参数或类上,就是通过数据绑定进行的),开发者直接使用的场景并不多。如果想要深入理解 Spring 内部的运行机制,数据绑定是必须了解的一块内容。

体验数据绑定

先以如下例子体验数据绑定:

@Component
public class DemoDatabinder{
    
    
	public void demo() {
    
    
		// 数据Address 
		Address addr=new Address();
		addr.setProvince("四川");
		addr.setCity("成都");
		addr.setCounty("高新区");
		addr.setDesc("学习大道168");
		// 待绑定的数据driver
		Driver driver=new Driver();

		/* 准备绑定的数据(实际应用中看做前端输入的数据) */
		Map<String, Object> input = new HashMap<>();
        input.put("surname", "wang");
        input.put("name", "wang");
        // age是整形  注1
        input.put("age", 20);
        // address是对象
        input.put("address", addr);

		/* 绑定数据 */
        //把准备绑定的数据转换成绑定PropertyValues 
        PropertyValues propertyValues = new MutablePropertyValues(input);
		// 用DataBinder 进行绑定
		DataBinder dataBinder = new DataBinder(driver);
        dataBinder.bind(propertyValues);	
        BindingResult br = dataBinder.getBindingResult();	
		if(br.hasErrors())
			System.out.println("数据绑定错误");
	}
}

注1:age是整形,输入值可以是整形20,也可以是字符串“20”或“20 ”(包含空格)
从上例中可看出,数据绑定就是把输入数据(input)绑定到对象(driver)的对应属性上,绑定有以下要求:
1、输入数据名同对象属性名相同。
2、输入数据值的数据类型同对象属性的数据类型可以不一致,绑定会自动转换。

Spring数据绑定

功能

Spring中,DataBinder有两大功能:
1、设置对象属性值。
2、对数据进行校验(数据校验见:https://blog.csdn.net/davidwkx/article/details/131401699)。

作用

Spring作为一个成熟框架,数据绑定是重要特性之一,数据绑定主要作用体现如下:
1、在WEB环境下,Spring可自动把Http中的请求参数或者请求体中的Json字符串绑定到对应实体对象上,这样可大大提高开发人员的效率,这是开发人员直观感知到的数据绑定。非WEB环境,开发人员也可按需使用数据绑定技术,只不过很少使用而已。
2、Spring框架本身也用到数据绑定:在xml中配置bean属性或注解配置属性,也是用的数据绑定来实现的。

类关系图

在这里插入图片描述

从图中可以看出,除了DataBinder使用的数据PropertyValues,关系图比较简单,只是实现了PropertyEditorRegistry, TypeConverter。
BindingResult继承于Errors,保存绑定结果,包括错误信息。

PropertyEditorRegistry

设置bean属性值的属性编辑器,代码如下:

public interface PropertyEditorRegistry {
    
    
	// 为给定类型的所有属性设置属性编辑器
	void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
	void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);
	// 获取给定类型低属性编辑器
	@Nullable
	PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}

TypeConverter

转换数据到指定的类型,接口定义简单(仅参数不同):

public interface TypeConverter {
    
    
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable MethodParameter methodParam) throws TypeMismatchException;
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)	throws TypeMismatchException;
	@Nullable
	default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
    
    
		throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
	}
}

源码跟踪

DataBinder.bind(PropertyValues pvs)

	// 过渡类
	public void bind(PropertyValues pvs) {
    
    
		MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues mutablePropertyValues ?
				mutablePropertyValues : new MutablePropertyValues(pvs));
		doBind(mpvs);
	}
	// 绑定处理
	protected void doBind(MutablePropertyValues mpvs) {
    
    
		// 根据允许绑定的字段检查给定的属性值,删除不允许的字段的值
		checkAllowedFields(mpvs);
		// 根据所需绑定字段检查给定的属性值,对于必须有值的字段如果没有值,生成缺少值的错误信息
		checkRequiredFields(mpvs);
		// 赋值到对象属性
		applyPropertyValues(mpvs);
	}
	// 过渡类
	protected void applyPropertyValues(MutablePropertyValues mpvs) {
    
    
		try {
    
    
			// Bind request parameters onto target object.
			// 赋值到对象属性 注1
			getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
		}
		catch (PropertyBatchUpdateException ex) {
    
    
			// 绑定过程发生错误,则生成错误放到BindingResult
			for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
    
    
				getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
			}
		}
	}

注1:此处实际调用BeanWrapperImpl.setPropertyValues,其类关系图如下:
在这里插入图片描述
BeanWrapperImpl是BeanWrapper的默认实现,BeanWrapper是Spring服务于JavaBeans的底层接口,主要提供分析和操作JavaBeans,包括:获取和设置属性值(单独或批量)、获取属性描述符以及查询属性可读性/可写性的能力等。BeanWrapperImpl关系图比较复杂,这里不具体分析,只关注数据绑定用到的实现在AbstractPropertyAccessor.setPropertyValues

AbstractPropertyAccessor.setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)

设置对象属性。

	@Override
	public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {
    
    

		List<PropertyAccessException> propertyAccessExceptions = null;
		// 把数据转换成MutablePropertyValues 
		List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues mpvs ?
				mpvs.getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));

		if (ignoreUnknown) {
    
    
			this.suppressNotWritablePropertyException = true;
		}
		try {
    
    
			// 按数据逐一设置
			for (PropertyValue pv : propertyValues) {
    
    
				try {
    
    
					setPropertyValue(pv);
				}
				catch (NotWritablePropertyException ex) {
    
    
					if (!ignoreUnknown) {
    
    
						throw ex;
					}
					// Otherwise, just ignore it and continue...
				}
				catch (NullValueInNestedPathException ex) {
    
    
					if (!ignoreInvalid) {
    
    
						throw ex;
					}
					// Otherwise, just ignore it and continue...
				}
				catch (PropertyAccessException ex) {
    
    
					if (propertyAccessExceptions == null) {
    
    
						propertyAccessExceptions = new ArrayList<>();
					}
					propertyAccessExceptions.add(ex);
				}
			}
		}
		finally {
    
    
			if (ignoreUnknown) {
    
    
				this.suppressNotWritablePropertyException = false;
			}
		}

		// 如果有属性可访问性异常,组合成一个异常抛出
		if (propertyAccessExceptions != null) {
    
    
			PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);
			throw new PropertyBatchUpdateException(paeArray);
		}
	}
	// 过渡方法
	@Override
	public void setPropertyValue(PropertyValue pv) throws BeansException {
    
    
		// 实现在AbstractNestablePropertyAccessor 
		setPropertyValue(pv.getName(), pv.getValue());
	}

AbstractNestablePropertyAccessor .setPropertyValue(String propertyName, @Nullable Object value)

	// 列出这个类便于后续理解-AbstractNestablePropertyAccessor内部类:用于存储属性token的Holder类
	protected static class PropertyTokenHolder {
    
    
		public PropertyTokenHolder(String name) {
    
    
			this.actualName = name;
			this.canonicalName = name;
		}
		public String actualName;
		public String canonicalName;
		@Nullable
		public String[] keys;
	}
	// 本类设置属性值入口
	@Override
	public void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException {
    
    
		AbstractNestablePropertyAccessor nestedPa;
		try {
    
    
			// 获取属性访问器
			nestedPa = getPropertyAccessorForPropertyPath(propertyName);
		}
		catch (NotReadablePropertyException ex) {
    
    
			throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
					"Nested property in path '" + propertyName + "' does not exist", ex);
		}
		// 将属性名称解析为相应的属性名称标记PropertyTokenHolder 
		PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
		// setPropertyValue在AbstractNestablePropertyAccessor 实现
		nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
	}
	// 分两种情况处理
	protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
    
    
		if (tokens.keys != null) {
    
    
			// 集合类属性赋值
			processKeyedProperty(tokens, pv);
		}
		else {
    
    
			// 单个(非集合类)属性赋值
			processLocalProperty(tokens, pv);
		}
	}
	// 集合类属性赋值
	@SuppressWarnings({
    
    "unchecked", "rawtypes"})
	private void processKeyedProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    
    
		Object propValue = getPropertyHoldingValue(tokens);
		PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
		if (ph == null) {
    
    
			throw new InvalidPropertyException(
					getRootClass(), this.nestedPath + tokens.actualName, "No property handler found");
		}
		Assert.state(tokens.keys != null, "No token keys");
		String lastKey = tokens.keys[tokens.keys.length - 1];

		// 属性是数组类型
		if (propValue.getClass().isArray()) {
    
    
			Class<?> requiredType = propValue.getClass().getComponentType();
			int arrayIndex = Integer.parseInt(lastKey);
			Object oldValue = null;
			try {
    
    
				if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
    
    
					oldValue = Array.get(propValue, arrayIndex);
				}
				// 转换数据为属性值类型
				Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
						requiredType, ph.nested(tokens.keys.length));
				int length = Array.getLength(propValue);
				if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
    
    
					Class<?> componentType = propValue.getClass().getComponentType();
					Object newArray = Array.newInstance(componentType, arrayIndex + 1);
					System.arraycopy(propValue, 0, newArray, 0, length);
					int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
					String propName = tokens.canonicalName.substring(0, lastKeyIndex);
					setPropertyValue(propName, newArray);
					propValue = getPropertyValue(propName);
				}
				// Array赋值
				Array.set(propValue, arrayIndex, convertedValue);
			}
			catch (IndexOutOfBoundsException ex) {
    
    
				throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
						"Invalid array index in property path '" + tokens.canonicalName + "'", ex);
			}
		}
		// 属性是list类型
		else if (propValue instanceof List list) {
    
    
			Class<?> requiredType = ph.getCollectionType(tokens.keys.length);
			int index = Integer.parseInt(lastKey);
			Object oldValue = null;
			if (isExtractOldValueForEditor() && index < list.size()) {
    
    
				oldValue = list.get(index);
			}
			Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
					requiredType, ph.nested(tokens.keys.length));
			int size = list.size();
			if (index >= size && index < this.autoGrowCollectionLimit) {
    
    
				for (int i = size; i < index; i++) {
    
    
					try {
    
    
						list.add(null);
					}
					catch (NullPointerException ex) {
    
    
						throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
								"Cannot set element with index " + index + " in List of size " +
								size + ", accessed using property path '" + tokens.canonicalName +
								"': List does not support filling up gaps with null elements");
					}
				}
				// list赋值-增加
				list.add(convertedValue);
			}
			else {
    
    
				try {
    
    
					// list赋值-增加或修改
					list.set(index, convertedValue);
				}
				catch (IndexOutOfBoundsException ex) {
    
    
					throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
							"Invalid list index in property path '" + tokens.canonicalName + "'", ex);
				}
			}
		}
		// 属性是Map 类型
		else if (propValue instanceof Map map) {
    
    
			Class<?> mapKeyType = ph.getMapKeyType(tokens.keys.length);
			Class<?> mapValueType = ph.getMapValueType(tokens.keys.length);
			// IMPORTANT: Do not pass full property name in here - property editors
			// must not kick in for map keys but rather only for map values.
			TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
			Object convertedMapKey = convertIfNecessary(null, null, lastKey, mapKeyType, typeDescriptor);
			Object oldValue = null;
			if (isExtractOldValueForEditor()) {
    
    
				oldValue = map.get(convertedMapKey);
			}
			// Pass full property name and old value in here, since we want full
			// conversion ability for map values.
			Object convertedMapValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
					mapValueType, ph.nested(tokens.keys.length));
			// map赋值
			map.put(convertedMapKey, convertedMapValue);
		}

		else {
    
    
			throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
					"Property referenced in indexed property path '" + tokens.canonicalName +
					"' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");
		}
	}
	// 单个(非集合类)属性赋值
	private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    
    
		PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
		if (ph == null || !ph.isWritable()) {
    
    
			if (pv.isOptional()) {
    
    
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Ignoring optional value for property '" + tokens.actualName +
							"' - property not found on bean class [" + getRootClass().getName() + "]");
				}
				return;
			}
			if (this.suppressNotWritablePropertyException) {
    
    
				// Optimization for common ignoreUnknown=true scenario since the
				// exception would be caught and swallowed higher up anyway...
				return;
			}
			throw createNotWritablePropertyException(tokens.canonicalName);
		}

		Object oldValue = null;
		try {
    
    
			Object originalValue = pv.getValue();
			Object valueToApply = originalValue;
			if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
    
    
				/* 数据类型转换 */
				if (pv.isConverted()) {
    
    
					// 已转换直接取转换后的值
					valueToApply = pv.getConvertedValue();
				}
				else {
    
    
					if (isExtractOldValueForEditor() && ph.isReadable()) {
    
    
						try {
    
    
							oldValue = ph.getValue();
						}
						catch (Exception ex) {
    
    
							if (ex instanceof PrivilegedActionException pae) {
    
    
								ex = pae.getException();
							}
							if (logger.isDebugEnabled()) {
    
    
								logger.debug("Could not read previous value of property '" +
										this.nestedPath + tokens.canonicalName + "'", ex);
							}
						}
					}
					// 数据类型转换
					valueToApply = convertForProperty(
							tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
				}
				pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
			}
			// 赋值
			ph.setValue(valueToApply);
		}
		catch (TypeMismatchException ex) {
    
    
			throw ex;
		}
		catch (InvocationTargetException ex) {
    
    
			PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
					getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
			if (ex.getTargetException() instanceof ClassCastException) {
    
    
				throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
			}
			else {
    
    
				Throwable cause = ex.getTargetException();
				if (cause instanceof UndeclaredThrowableException) {
    
    
					// May happen e.g. with Groovy-generated methods
					cause = cause.getCause();
				}
				throw new MethodInvocationException(propertyChangeEvent, cause);
			}
		}
		catch (Exception ex) {
    
    
			PropertyChangeEvent pce = new PropertyChangeEvent(
					getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
			throw new MethodInvocationException(pce, ex);
		}
	}

注:关于数据类型转换详见:https://blog.csdn.net/davidwkx/article/details/131913840

猜你喜欢

转载自blog.csdn.net/davidwkx/article/details/131913078