Spring源码解析之-BeanWrapper分析

一、解释以及例子

BeanWrapper 相当于是Spring中的一个包装类,对Bean 进行包装,具有(单独或批量)获取和设置属性值,获取属性描述符以及查询属性的可读性/可写性的能力。还可以进行类型的转换等功能。 一般Bean 的创建 都是BeanDefinition ==> BeanWrapper ==>Bean 这三个步骤。 BeanWrapper 之所以具备编辑、类型转换 等这些功能,是由于继承了ConfigurablePropertyAccessor、PropertyAccessor, PropertyEditorRegistry, TypeConverter 这些接口.
在这里插入图片描述

1.1 例子

新建一个学生类 Student.java


public class Student {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

再建一个学校类School.java


public class School {
    private String schoolName;
    private Student[] students;

    public String getSchoolName() {
        return schoolName;
    }
    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }
    public Student[] getStudents() {
        return students;
    }
    public void setStudents(Student[] students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "School{" +
                "schoolName='" + schoolName + '\'' +
                ", students=" + Arrays.toString(students) +
                '}';
    }
}

最后写个Main 测试一下

public static void main(String[] args) {
        //第一个学生
        BeanWrapper beanWrapper = new BeanWrapperImpl(new Student());
        PropertyValue pv = new PropertyValue("name","jack");
        PropertyValue pv1 = new PropertyValue("age",16);
        beanWrapper.setPropertyValue(pv);
        beanWrapper.setPropertyValue(pv1);
        Student student = (Student) beanWrapper.getWrappedInstance();
        System.out.println(student.getAge());
        System.out.println(student.getName());

        //第二个学生
        BeanWrapper beanWrapper2 = new BeanWrapperImpl(new Student());
        PropertyValue student2pv = new PropertyValue("name","Tom");
        PropertyValue student2pv2 = new PropertyValue("age",18);
        beanWrapper2.setPropertyValue(student2pv);
        beanWrapper2.setPropertyValue(student2pv2);
        Student student2 = (Student) beanWrapper2.getWrappedInstance();
        System.out.println(student2.getAge());
        System.out.println(student2.getName());

        //学校
        BeanWrapper schoolBeanWrapper3 = new BeanWrapperImpl(new School());
        PropertyValue schoolpv = new PropertyValue("schoolName","学校");
        Student[] st = {student,student2};
        schoolBeanWrapper3.setPropertyValue(schoolpv);
        schoolBeanWrapper3.setPropertyValue("students",st);
        schoolBeanWrapper3.setPropertyValue("students[1].age",20);
        School school = (School) schoolBeanWrapper3.getWrappedInstance();
        System.out.println(school);
    }

结果如下:

16
jack
18
Tom
School{schoolName='学校', students=[Student{name='jack', age=16}, Student{name='Tom', age=20}]}

看完这个例子,大致对里面的方法有一些了解,接下来我们继续看源码的相关逻辑.

二、源码解读

2.1 BeanWrapper

  • BeanWrapper 是底层JavaBeans 的中央接口.
  • 一般不会被直接调用,而是隐含的被BeanFactory 或者DataBinder使用.
  • 具有(单独或批量)获取和设置属性值,获取属性描述符以及查询属性的可读性/可写性的能力.
  • 该接口支持嵌套属性,可将子属性上的属性设置为无限深度.
  • BeanWrapper的“ extractOldValueForEditor”默认值为false,以避免由getter方法调用引起的副作用。 将此选项设置为true,即自定义编辑器公开当前属性值。

BeanWrapper继承了ConfigurablePropertyAccessor、PropertyAccessor, PropertyEditorRegistry, TypeConverter 这些接口. BeanWrapper主要有如下方法:

public interface BeanWrapper extends ConfigurablePropertyAccessor {
	/**
	 * @since 4.1
	 * 指定数组和集合自动增长的限制。在普通的BeanWrapper上,默认值是无限的。
	 */
	void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
	/**
	 *返回数组和集合自动增长的限制。
	 * @since 4.1
	 */
	int getAutoGrowCollectionLimit();
	/**
	 * 返回此对象包装的bean实例
	 */
	Object getWrappedInstance();
	/**
	 * 返回包装的bean实例的类型
	 */
	Class<?> getWrappedClass();
	/**
	 * 返回包装类的属性描述(由标准JavaBean自省确定)
	 */
	PropertyDescriptor[] getPropertyDescriptors();
	/**
	 * 根据属性名称获取对应的 property descriptor 
	 */
	PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
}

BeanWrapper 的默认实现类BeanWrapperImpl ,这里 主要看一下getPropertyDescriptor(String propertyName) 的逻辑:

	public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
	// 获取对应属性的BeanWrapperImpl 
		BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
		// 获取需要获取的对应属性 名称
		String finalPath = getFinalPath(nestedBw, propertyName);
		// 从nestedBw 中获取对应的属性描述
		PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
		if (pd == null) {
			throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
					"No property '" + propertyName + "' found");
		}
		return pd;
	}

	protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
	    // 获取嵌套属性的第一个 属性
	    // 比如这里有一个为:school.students[0].age ,这里就要先获取到students 的index 位置
	    // 详细的方法在下面
		int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
		// Handle nested properties recursively.
		//递归处理嵌套属性
		if (pos > -1) {
		    // 获取所在的属性, 和 要 获取的属性对应的 name
			String nestedProperty = propertyPath.substring(0, pos);
			String nestedPath = propertyPath.substring(pos + 1);
			AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);
			//递归调用
			return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
		}
		else {
			return this;
		}
	}
	
/**
*根据路径获取对应的属性的索引
* 这里存在 students[0].age ,属性里面还有属性的嵌套情况,需要找到属性对应的index 位置
*/
private static int getNestedPropertySeparatorIndex(String propertyPath, boolean last) {
		boolean inKey = false;
		// 获取对应的属性的长度,用于下面的遍历
		int length = propertyPath.length();
		int i = (last ? length - 1 : 0);
		while (last ? i >= 0 : i < length) {
		    // 对所有的字符遍历进行解析,这里 增加一个inKey 的flag,要
		    // 对[ 、] 做成对的判断
			switch (propertyPath.charAt(i)) {
				case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR:
				case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR:
					inKey = !inKey;
					break;
				case PropertyAccessor.NESTED_PROPERTY_SEPARATOR_CHAR:
				// 如果出现'.' ,说明存在嵌套属性,并且inKey =false ,说明在这之前[ ] 是成对匹配的
			    // 返回对应的  index 位置
					if (!inKey) {
						return i;
					}
			}
			if (last) {
				i--;
			}
			else {
				i++;
			}
		}
		return -1;
	}

2.1 TypeConverter

TypeConverter 定义类型转换方法的接口:
通常(但不一定)与PropertyEditorRegistry接口结合实现。
由于TypeConverter实现通常基于不是线程安全的PropertyEditor,TypeConverters本身也不被认为是线程安全的。

public interface TypeConverter {
	/**
	 * 将值转换为所需的类型(如果需要从字符串)
     * <p>从字符串到任何类型的转换通常将使用{@code setAsText}
     * 在PropertyEditor类的方法,或ConversionService中的Spring Converter。
	 */
	@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");
	}

}

对应的实现在 TypeConverterSupport类里面(DataBinder也有对应的实现,这里主要涉及上图相关的), 但是具体的实现都是在 TypeConverterDelegate 类里面 ,TypeConverterDelegate 的源码详见:
Spring源码解析之-TypeConverter、TypeConverterDelegate分析

2.2 PropertyEditorRegistry

封装用于注册JavaBeans 的方法,有如下方法:

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

从上面上,PropertyEditorRegistrySupport 实现了 PropertyEditorRegistry 相关的方法,

2.3 PropertyAccessor

访问定义的属性的的通用接口,例如:一个对象的bean 属性或者 一个字段
/**
 * Common interface for classes that can access named properties
 * (such as bean properties of an object or fields in an object)
 * 访问定义的属性的的通用接口
 * 例如:一个对象的bean 属性或者 一个字段
 */
 
public interface PropertyAccessor {
	/**
	 * 嵌套属性的路径分隔符
	 * 遵循正常的Java约定:getFoo().getBar() would be "foo.bar"
	 */
	String NESTED_PROPERTY_SEPARATOR = ".";
	char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
	/**
	 * 标记,用于指示属性键的开始
	 * 索引或映射的属性,例如"person.addresses[0]".
	 */
	String PROPERTY_KEY_PREFIX = "[";
	char PROPERTY_KEY_PREFIX_CHAR = '[';
	/**
	 * 标记,用于指示属性键的结尾
	 * 索引或映射的属性,例如"person.addresses[0]".
	 */
	String PROPERTY_KEY_SUFFIX = "]";
	char PROPERTY_KEY_SUFFIX_CHAR = ']';
	/**
	 * 确定指定的属性是否可读
	 */
	boolean isReadableProperty(String propertyName);

	/**
	 * 确定指定的属性是否可写
	 */
	boolean isWritableProperty(String propertyName);

	/**
	 * 通过 检查属性的描述相关或者 检查值 来 确定指定属性的属性类型
	 */
	@Nullable
	Class<?> getPropertyType(String propertyName) throws BeansException;

	@Nullable
	TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;

	@Nullable
	Object getPropertyValue(String propertyName) throws BeansException;

	void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;

	void setPropertyValue(PropertyValue pv) throws BeansException;

	void setPropertyValues(Map<?, ?> map) throws BeansException;

	void setPropertyValues(PropertyValues pvs) throws BeansException;
	
	void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
			throws BeansException;

	void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException;
}

2.4 ConfigurablePropertyAccessor

public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
	/**
	 * 指定 Spring 3.0 ConversionService来转换属性值,以替代JavaBeans PropertyEditor。
	 */
	void setConversionService(@Nullable ConversionService conversionService);
	/**
	 * 返回关联的ConversionService(如果有)
	 */
	@Nullable
	ConversionService getConversionService();
	/**
	 * 设置在将属性编辑器应用于属性的新值时,是否提取旧属性值。
	 */
	void setExtractOldValueForEditor(boolean extractOldValueForEditor);

	boolean isExtractOldValueForEditor();

	void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);

	boolean isAutoGrowNestedPaths();

}

三、总结

BeanWrapper 相当于是Spring中的一个包装类,对Bean 进行包装,具有(单独或批量)获取和设置属性值,获取属性描述符以及查询属性的可读性/可写性的能力。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直打铁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值