Spring源码阅读(八)

摘要: 本文首先将举例说明如何使用BeanWrapper,然后根据例子中的结果分析BeanWrapper的源码。由于在spring中BeanWrapperImpl是BeanWrapper接口的唯一实现类,所以将对BeanWrapperImpl进行源码分析。最后,将从三个方面分析BeanWrapperImpl的源码,分别是构造方法、设置属性和获取属性。

1、BeanWrapper简介

BeanWrapper接口,作为spring内部的一个核心接口,正如其名,它是bean的包裹类,即在内部中将会保存该bean的实例,提供其它一些扩展功能。同时,BeanWrapper接口还继承了PropertyAccessor, propertyEditorRegistry, TypeConverter、ConfigurablePropertyAccessor接口,所以它还提供了访问bean的属性值、属性编辑器注册、类型转换等功能。

下面我们一起回顾一下bean的实例化过程,看一下spring是怎么使用BeanWrapper。

                                                         bean的实例化过程

(1)ResourceLoader加载配置信息

(2)BeanDefinitionReader读取并解析<bean>标签,并将<bean>标签的属性转换为BeanDefinition对应的属性,并注册到BeanDefinitionRegistry注册表中。

(3)容器扫描BeanDefinitionRegistry注册表,通过反射机制获取BeanFactoryPostProcessor类型的工厂后处理器,并用这个工厂后处理器对BeanDefinition进行加工。

(4)根据处理过的BeanDefinition,实例化bean。然后BeanWrapper结合BeanDefinitionRegistry和PropertyEditorRegistry对Bean的属性赋值。

在上面的bean实例化过程中,BeanWrapper取出XML中定义的值,然后通过属性编辑器或类型转换器把xml中的字符串值转换成bean中属性对应的类型,最后以内省的方式设置到bean的属性。

其实,在BeanWrapper接口中,最核心的功能就是读取和设置bean的属性,它是通过java内省的方式完成bean属性的访问的。为了能够感性认识BeanWrapper接口,下面将通过一个例子看一下怎么使用BeanWrapper访问bean的属性。

2、BeanWrapper的使用

前面介绍过,BeanWrapper接口继承了PropertyAccessor接口,所以它具备了访问bean的属性的能力。我们看一下PropertyAccessor接口的一些方法:

public interface PropertyAccessor {

    String NESTED_PROPERTY_SEPARATOR = ".";
    char NESTED_PROPERTY_SEPARATOR_CHAR = '.';

    String PROPERTY_KEY_PREFIX = "[";
    char PROPERTY_KEY_PREFIX_CHAR = '[';

    String PROPERTY_KEY_SUFFIX = "]";
    char PROPERTY_KEY_SUFFIX_CHAR = ']';

    boolean isReadableProperty(String propertyName);

    boolean isWritableProperty(String propertyName);

    Class<?> getPropertyType(String propertyName) throws BeansException;

    TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;

    Object getPropertyValue(String propertyName) throws BeansException;

    void setPropertyValue(String propertyName, 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;

}

PropertyAccessor接口提供了访问bean属性的方法,还定义了访问嵌套属性的访问表达式的切割符,重点看一下下面两个方法:

Object getPropertyValue(String propertyName) throws BeansException;

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

getPropertyValue和setPropertyValue是分别用于获取和设置bean的属性值的。这里的propertyName支持表达式:

表达式说明

name

指向属性name,与getName() 或 isName() 和 setName()相对应。
account.name 指向属性account的嵌套属性name,与之对应的是getAccount().setName()和getAccount().getName()
account[2] 指向索引属性account的第三个元素,索引属性可能是一个数组(array),列表(list)或其它天然有序的容器。
account[COMPANYNAME] 指向一个Map实体account中以COMPANYNAME作为键值(key)所对应的值

举个例子。我们需要使用BeanWrapper访问它们的属性。

public class Car {
    private String name;
    private Wheel[] wheels;

    private Driver driver;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Wheel[] getWheels() {
        return wheels;
    }

    public void setWheels(Wheel[] wheels) {
        this.wheels = wheels;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver(Driver driver) {
        this.driver = driver;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", wheels=" + Arrays.toString(wheels) +
                ", driver=" + driver +
                '}';
    }
}
public class Driver {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Driver{" +
                "age=" + age +
                '}';
    }
}
public class Wheel {
    private String position;

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    @Override
    public String toString() {
        return "Wheel{" +
                "position='" + position + '\'' +
                '}';
    }
}
 

   public static void main(String[] args) {

        //左边轮子的BeanWrapper
        Wheel leftWheel = new Wheel();
        BeanWrapper beanWrapperOfLeftWheel = PropertyAccessorFactory.forBeanPropertyAccess(leftWheel);
        PropertyValue leftPosition = new PropertyValue("position", "左边");
        beanWrapperOfLeftWheel.setPropertyValue(leftPosition);
        System.out.println(beanWrapperOfLeftWheel.getWrappedInstance());

        //左边轮子的BeanWrapper
        Wheel rightWheel = new Wheel();
        BeanWrapper beanWrapperOfRightWheel = PropertyAccessorFactory.forBeanPropertyAccess(rightWheel);
        PropertyValue rightPosition = new PropertyValue("position", "右边");
        beanWrapperOfRightWheel.setPropertyValue(rightPosition);
        System.out.println(beanWrapperOfRightWheel.getWrappedInstance());

        // 驾驶员
        Driver driver = new Driver();
        BeanWrapper beanWrapperOfDriver = PropertyAccessorFactory.forBeanPropertyAccess(driver);
        PropertyValue age = new PropertyValue("age", 20);
        beanWrapperOfDriver.setPropertyValue(age);
        System.out.println(beanWrapperOfDriver.getWrappedInstance());


        // 车子
        Car car = new Car();
        BeanWrapper beanWrapperOfCar = PropertyAccessorFactory.forBeanPropertyAccess(car);
        beanWrapperOfCar.setPropertyValue("name", "奔驰"); // 车名
        Wheel[] wheels = {leftWheel, rightWheel}; //轮子数组
        beanWrapperOfCar.setPropertyValue("wheels", wheels); //轮子
        beanWrapperOfCar.setPropertyValue("driver", driver); //驾驶员
        System.out.println(beanWrapperOfCar.getWrappedInstance());

        // 获取驾驶员的年龄
        int retrievedAge = (Integer) beanWrapperOfCar.getPropertyValue("driver.age");
        System.out.println("driver age : " + retrievedAge);


        // 通过表达式间接设置car的wheel的width
        beanWrapperOfCar.setPropertyValue("wheels[0].position", "修改过的左边");
        System.out.println(beanWrapperOfCar.getWrappedInstance());

    }

上面的代码已经很清楚地演示了如何使用BeanWrapper设置和获取bean的属性,有了初步的认识之后,接下来将会解析该源码。

3、解析BeanWrapper源码

BeanWrapper的实现类是BeanWrapperImpl,它包装了bean对象,缓存了bean的内省结果,并可以访问bean的属性、设置bean的属性值。除此之外,BeanWrapperImpl类提供了许多默认属性编辑器,支持多种不同类型的类型转换,例如,可以将数组、集合类型的属性转换成指定特殊类型的数组或集合。用户也可以注册自定义的属性编辑器在BeanWrapperImpl中。

另外,BeanWrapperImpl有一个cachedIntrospectionResults成员变量,它保存了被包装bean的内省分析结果。cachedIntrospectionResults有两个成员变量,一个是beanInfo,它是被包裹类的BeanInfo;另一个是propertyDescriptorCache,它缓存了被包裹类的所有属性的属性描述器PropertyDescriptor。

 

我们看一下BeanWrapperImpl的结构类图。

3.1 构造方法

BeanWrapperImpl重载了很多种构造方法,我们看一下通过传入bean实例作为参数的构造方法。

    public BeanWrapperImpl(Object object) {
        super(object);
    }

调用父类AbstractNestablePropertyAccessor的构造方法。

    protected AbstractNestablePropertyAccessor(Object object) {
        registerDefaultEditors(); // 标识可以使用默认的属性编辑器
        setWrappedInstance(object);
    }
    public void setWrappedInstance(Object object) {
        setWrappedInstance(object, "", null);
    }
    public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
        
        // bean设置为BeanWrapperImpl的内部变量
        if (object.getClass() == javaUtilOptionalClass) {
            this.wrappedObject = OptionalUnwrapper.unwrap(object);
        }
        else {
            this.wrappedObject = object;
        }

        this.nestedPath = (nestedPath != null ? nestedPath : "");
        this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.wrappedObject);
        this.nestedPropertyAccessors = null;
         
        // 新建类型转换器的委托类,这里BeanWrapperImpl的实例为propertyEditorRegistry,bean为targetObject
        this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
    }

很明显,构造方法中做了两件重要的事,一个是把bean设为内部变量,另一个是实例化了一个类型转换器的委托类,由于BeanWrapperImpl同时继承了PropertyEditorRegistrySupport,所以它作为TypeConverterDelegate的属性编辑器注册中心的帮助类存在于TypeConverterDelegate中。

3.2 设置属性

BeanWrapperImpl支持多种设置bean属性的方法。

先看一下 void setPropertyValue(String propertyName, Object value)

public void setPropertyValue(String propertyName, Object value) throws BeansException {
    AbstractNestablePropertyAccessor nestedPa;
    try { 
        //根据属性名获取BeanWrapImpl对象,支持多重属性的递归分析处理
        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));
    nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}

getPropertyAccessorForPropertyPath根据属性名的表达式获取访问该属性的属性访问器AbstractNestablePropertyAccessor ,即BeanWrapperImpl 。下面的注释如果没有明白的话,请先往下看,然后再回头看这里,一切豁然开朗。

下面看一下 getPropertyAccessorForPropertyPath 是怎么处理内嵌属性的,即对象里面包含对象。

protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
    // 获取第一个内嵌属性的位置,分隔符是"."
    int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
    
    // 递归获取内嵌属性,如果propertyPath不再存在分隔符“.”,返回递归结果
    if (pos > -1) {
        
        // 假设propertyPath为 wheels[0].position
        String nestedProperty = propertyPath.substring(0, pos); // wheels[0].position -> wheels[0]
        
        String nestedPath = propertyPath.substring(pos + 1); // wheels[0].position -> position
        
        // 获取本轮递归中AbstractNestablePropertyAccessor的属性
        AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);
        
        return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
    }
    else {
        return this;
    }
}

该方法递归获取需要访问属性的AbstractNestablePropertyAccessor。一起看一下getNestedPropertyAccessor是怎么根据属性名获取到属性访问器的。

private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
    if (this.nestedPropertyAccessors == null) {
        this.nestedPropertyAccessors = new HashMap<String, AbstractNestablePropertyAccessor>();
    }
    
    PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty); //根据属性名获取PropertyTokenHolder
    
    String canonicalName = tokens.canonicalName;
    
    Object value = getPropertyValue(tokens); // 根据PropertyTokenHolder获取该内嵌属性的实例化对象
    
    // 该属性在bean中为null
    if (value == null || (value.getClass() == javaUtilOptionalClass && OptionalUnwrapper.isEmpty(value))) {
        // 如果允许自动创建属性,调用setDefaultValue创建默认的对象,否则抛异常
        if (isAutoGrowNestedPaths()) { 
            value = setDefaultValue(tokens);
        }
        else {
            throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
        }
    }

    // 把上面获取到的内嵌对象的实例,包裹为一个新的BeanWrapperImpl,然后把该BeanWrapperImpl缓存到本级的缓存对象nestedPropertyAccessors。
    // 例如,上一节中的Car的driver属性,在car本级,维护了一个缓存对象nestedPropertyAccessors,
    // 第一次访问内嵌属性driver时,将会为driver创建PropertyAccessor,并缓存到nestedPropertyAccessors中。
    AbstractNestablePropertyAccessor nestedPa = this.nestedPropertyAccessors.get(canonicalName);
    if (nestedPa == null || nestedPa.getWrappedInstance() !=
            (value.getClass() == javaUtilOptionalClass ? OptionalUnwrapper.unwrap(value) : value)) {

        // 内嵌对象的实例,包裹为一个新的BeanWrapperImpl
        nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);

        // 新的BeanWrapperImpl 继承本级的PropertyEditors.
        copyDefaultEditorsTo(nestedPa);
        copyCustomEditorsTo(nestedPa, canonicalName);

        // 缓存
        this.nestedPropertyAccessors.put(canonicalName, nestedPa);
    }
    else {

    }
    return nestedPa;
}

在上面代码中,getPropertyNameTokens将根据propertyName生成一个统一操作的结构PropertyTokenHolder,此类保存了属性名解析后的结构。该结构的成员如下:

属性类型及名称

说明

String canonicalName

propertyName为wheels[0]时,canonicalName为wheels[0];

propertyName为driver时,canonicalName为driver

String actualName

propertyName为driver时,actualName为driver

String[] keys

propertyName为driver时,keys为null

上面的代码 Object value = getPropertyValue(tokens) 将根据 PropertyTokenHolder 获取指定property属性的实例,一起看下这段代码。

protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
    String propertyName = tokens.canonicalName;
    String actualName = tokens.actualName;
    
    // 获取PropertyHandler,内部实现是从cachedIntrospectionResults中取出该属性的PropertyDescriptor,
    // 然后取出属性的PropertyType, ReadMethod , WriteMethod
    PropertyHandler ph = getLocalPropertyHandler(actualName);
    
    // 属性不可读,抛异常
    if (ph == null || !ph.isReadable()) {
        throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
    }
    
    try {
        // 内省方式获取属性的实例
        Object value = ph.getValue();
        
        // 如果该属性是数组、list、set,tokens的keys是不为空的,keys将会保存需要访问的索引号,
        // 在map中,keys是一个字符串
        // 下面就是通过该索引号获取特定下标的属性值。
        if (tokens.keys != null) {
            if (value == null) {
                if (isAutoGrowNestedPaths()) {
                    value = setDefaultValue(tokens.actualName);
                }
                else {
                    throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
                            "Cannot access indexed value of property referenced in indexed " +
                                    "property path '" + propertyName + "': returned null");
                }
            }
            
            String indexedPropertyName = tokens.actualName;
            
            // apply indexes and map keys
            for (int i = 0; i < tokens.keys.length; i++) {
                String key = tokens.keys[i];
                if (value == null) {
                    throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
                            "Cannot access indexed value of property referenced in indexed " +
                                    "property path '" + propertyName + "': returned null");
                }
                else if (value.getClass().isArray()) { //处理数组
                    int index = Integer.parseInt(key);
                    value = growArrayIfNecessary(value, index, indexedPropertyName);
                    value = Array.get(value, index);
                }
                else if (value instanceof List) { //处理list
                    int index = Integer.parseInt(key);
                    List<Object> list = (List<Object>) value;
                    growCollectionIfNecessary(list, index, indexedPropertyName, ph, i + 1);
                    value = list.get(index);
                }
                else if (value instanceof Set) { //处理set
                    // Apply index to Iterator in case of a Set.
                    Set<Object> set = (Set<Object>) value;
                    int index = Integer.parseInt(key);

                    Iterator<Object> it = set.iterator();
                    for (int j = 0; it.hasNext(); j++) {
                        Object elem = it.next();
                        if (j == index) {
                            value = elem;
                            break;
                        }
                    }
                }
                else if (value instanceof Map) { //处理map
                    Map<Object, Object> map = (Map<Object, Object>) value;
                    Class<?> mapKeyType = ph.getResolvableType().getNested(i + 1).asMap().resolveGeneric(0);
                    // 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, key, mapKeyType, typeDescriptor);
                    value = map.get(convertedMapKey);
                }
                else {

                }
                indexedPropertyName += PROPERTY_KEY_PREFIX + key + PROPERTY_KEY_SUFFIX;
            }
        }
        return value;
    }

    //异常,忽略
}

总结:在通过setPropertyValue设置bean的属性时,首先将会根据propertyName的字符串,递归获取该属性所在的内嵌属性(假如属性不在内嵌属性,获取的就是它自己),然后通过内省的方式设置该属性的值。

3.3 获取属性

BeanWrapperImpl有两个获取属性的方法

public Object getPropertyValue(String propertyName) throws BeansException

protected Object getPropertyValue(PropertyTokenHolder tokens)

其中 getPropertyValue(PropertyTokenHolder tokens) 在上面设置属性那一节已经出现过了,它是在内部使用的,不对外公开。

既然如此,看一下Object getPropertyValue(String propertyName)这个方法吧。该方法在AbstractNestablePropertyAccessor中实现。

    public Object getPropertyValue(String propertyName) throws BeansException {
        AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName);
        PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
        return nestedPa.getPropertyValue(tokens);
    }

其中,getPropertyAccessorForPropertyPath方法在上一节中已经有详细的解析,它将递归获取可以访问该属性的AbstractNestablePropertyAccessor,这里的实现类是BeanWrapperImpl。例如propertyName是driver.age,那么BeanWrapperImpl所包裹的就是Driver的实例。然后,根据PropertyTokenHolder获取属性值。这个流程与设置属性的相似,请参考前面。

转自:https://my.oschina.net/thinwonton/blog/1492224

posted @ 2017-10-18 23:25  coshaho  阅读(372)  评论(0)    收藏  举报