Spring 学习 (二)__BeanWrapper及其实现

一、 BeanWrapper

BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器,由于BeanWrapper接口是PropertyAccessor的子接口,因此其也可以设置以及访问被包装对象的属性值。BeanWrapper大部分情况下是在spring ioc内部进行使用,通过BeanWrapper,spring ioc容器可以用统一的方式来访问bean的属性。用户很少需要直接使用BeanWrapper进行编程。


二、 BeanWrapperImpl

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

三、 BeanWrapper类图


四、 BeanWrapperImpl设置所包装的bean属性值序列图



五、 BeanWrapperImpl构造方法源码分析

5.1     BeanWrapperImpl构造方法

BeanWrapperImpl类有多个重载方法,下面的构造方法传入一个Object对象,此对象就是被BeanWrapperImpl类所包装的bean

public BeanWrapperImpl(Object object) {
       registerDefaultEditors();
       setWrappedInstance(object);
    }

构造方法的实现很简单,第一步在registerDefaultEditors()方法内部设置属性defaultEditorsActive值为true

第二步则调用setWrappedInstance(object)方法,进行初始化以及设置被包装的对象

5.2    setWrappedInstance(object)方法

此方法内部对BeanWrapperImpl类的一些重要属性进行了初始化,并创建了TypeConverterDelegate类的实例作为类型转换处理对象。在此之后,将对被包装bean进行内省分析,内省分析结果保存在cachedIntrospectionResults属性中,此属性是CachedIntrospectionResults类的实例.


5.3    CachedIntrospectionResults

CachedIntrospectionResults类用于对对象的Class进行内省分析,保存对象的PropertyDescriptor信息,其静态方法

static CachedIntrospectionResults forClass(Class beanClass)

 throws BeansException

BeanWrapperImpl类中被调用,用于对BeanWrapperImpl类所包装的bean进行内省分析,并返回内省分析结果给BeanWrapperImpl


六、 BeanWrapperImpl设置属性值源码分析

6.1                   setPropertyValue(PropertyValue pv)方法

BeanWrapperImpl类有多个设置bean属性值的重载方法,此处以

public void setPropertyValue(PropertyValue pv)

throws BeansException

方法作为说明。

public void setPropertyValue(PropertyValue pv) throws BeansException {
       PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
       if (tokens == null) {
           ……………..
            BeanWrapperImpl nestedBw = null;
           try {
              //根据属性名获取BeanWrapImpl对象,支持多重属性的递归分析处理
              nestedBw = getBeanWrapperForPropertyPath(propertyName);
           }
           catch ………..
           tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
           //如果nestedBw等于this,则设置resolvedTokens属性值为tokens
           if (nestedBw == this) {
              pv.getOriginalPropertyValue().resolvedTokens = tokens;
           }
           nestedBw.setPropertyValue(tokens, pv);
       }
       else {
           setPropertyValue(tokens, pv);
       }
    }

上述方法根据tokens变量是否为null,有两个不同的分支。其中当tokensnull时,则会对属性名进行递归调用分析处理,返回分析处理后的BeanWrapImpl对象nestedBw。如果nestedBw==this,则会设置pvresolvedTokens属性值,最后将调用nestedBw对象的设置属性值方法设置属性

6.2                   getBeanWrapperForPropertyPath方法

getBeanWrapperForPropertyPath方法用于对属性名称(包括多重属性)进行处理,并返回BeanWrapperImpl对象。所支持的属性名称包括:多重属性(.分隔)、数组集合map key属性([]方式)

protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) {
       //根据属性路径获取其第一个属性分隔符.的下标
       int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
       //如果找到,则表示是有多重属性递归处理
       // Handle nested properties recursively.
       if (pos > -1) {
           //获取第一个属性分隔符前面的属性名称
           String nestedProperty = propertyPath.substring(0, pos);
           //获取第一个属性分隔符后面的字符串
           String nestedPath = propertyPath.substring(pos + 1);
           //获取第一个属性片断的BeanWrapperImpl对象nestedBw
           BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
           //调用nestedBw的getBeanWrapperForPropertyPath方法,对第一个属性分隔符后面的属性字符串进行处理
           return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
       }
       //否则,返回this对象本身
       else {
           return this;
       }
    }

在此方法中对嵌套类型的属性进行了分析,首先将处理第一个.前的属性,获取nestedBw对象。然后再调用nestedBw对象的此方法递归处理第一个.后的其它属性,并返回处理结果。

       getNestedBeanWrapper(nestedProperty)方法则根据nestedProperty获取嵌套的BeanWrapperImpl对象。

6.3                   getPropertyNameTokens方法

getPropertyNameTokens方法内部用于对属性名全路径中最后一个.后的属性名称分析,返回PropertyTokenHolder对象。

6.4                   setPropertyValue(tokens, pv)方法

最终的设置属性的操作在此方法内部进行实现,此方法将最原始的属性值经过数组、集合类型属性的处理和类型转换后得到转换后值convertedValue,并从内省信息中获取操作此属性的方法writeMethod,用反射调用writeMethod将参数值convertedValue写入至被包装对象的目标属性中。至此BeanWrapperImpl的对象设值操作处理完成。

七、 BeanWrapperImpl对嵌套属性的支持

BeanWrapperImpl类通过其成员属性提供了一种支持嵌套属性的数据结构,下面是BeanWrapperImpl类的成员:

属性类型及名称

说明

Object object

被BeanWrapper包装的对象

String nestedPath

当前BeanWrapper对象所属嵌套层次的属性名,最顶层的BeanWrapper此属性的值为空

Object rootObject

最顶层BeanWrapper所包装的对象

Map nestedBeanWrappers

缓存当前BeanWrapper的下一层属性的nestedPath和对应的BeanWrapperImpl对象,此BeanWrapperImpl所包装的对应是属性nestedPath的值


例如有类:

Class Employee

{

        Company company;

       Card card;

    String name;

       String id;

    get/set方法

}

Class Company

{

        String companyName;

        Map<String,String> attrs;

get/set方法

}

Class Card

{

         ………….

}

嵌套属性:employee.company.attrs[“location”]

最顶层的BeanWrapperemployeeBeanWrapper,其包装的对象是employeeObj,nestedPath为空,rootObject也是employObj, employeeBeanWrapper里的nestedBeanWrappers则将包含以下的键值对:

“company”---àcompanyBeanWrapper

“card” ---àcardBeanWrapper

对于对象companyBeanWrapper,则其包装的对象则是companyObj,其nestedPath的值为company,rootObject为employeeObj, nestedBeanWrappers里面的值为空。

BeanWrapperImpl在调用方法

protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath)

分析employee.company.attrs[“location”]属性值时,将先处理第一个点之前的属性employee得到顶层的BeanWrapper nestedBw,再调用nestedBw. getBeanWrapperForPropertyPath()方法递归处理第一个点后的其它属性,并最终返回所有属性段都分析后所产生的BeanWrapperImpl对象。

属性类型及名称

说明

String canonicalName

canonicalName为actualName再加上[key1][key2][key3]..这种形式

保存当前级别属性的实际名称,为[前的字符串

如属性employee.company.attrs[“location”]

EmployeeBeanWrapperImpl类代表company属性,则canonicalName值为”company”

如在CompanyBeanWrapperImpl类代表attrskeylocation的属性,则其canonicalNameattrs[“location”]

 

String actualName

保存当前级别属性的实际名称,为[前的字符串

如属性employee.company.attrs[“location”]

EmployeeBeanWrapperImpl类代表company属性,则actualName值为”company”

如在CompanyBeanWrapperImpl类代表attrskeylocation的属性,则其actualName”attrs”,取当前属性级别中”[“前面的字符串

 

String[] keys

代表当前级别属性中所有位于[与]间的key或索引所组成的数组

注:“当前级别属性”表示所要访问在属性在属性全路径中位于..之间的值

九、 所使用到的工具类

在此过程中所使用到的一些工具类和主要方法如下:

org.springframework.beans.PropertyAccessorUtils  用于分析属性名称的工具类,提供分析嵌套属性、集合属性(包括.[]等)的一些工具方法。



展开阅读全文

没有更多推荐了,返回首页