目录
一、解释以及例子
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 进行包装,具有(单独或批量)获取和设置属性值,获取属性描述符以及查询属性的可读性/可写性的能力。