爱心技术专栏专题

struts源代码阅读(Commons-Beanutils包)

摘录:java基础 来源:java基础 加入时间:2006年07月25日
摘要:
struts源代码阅读(Commons-Beanutils包)

摘要:
既然是说Struts源代码,为什么要讲Commons-Beanutils包呢?原因很简单,Struts的DynaFormBean就是通过这个包里的相关类实现的。本文对Commons-Beanutils 源代码进行了分析..
既然是说Struts源代码,为什么…

转载:转载请保留本信息,本文来自
http://www.51dibs.com
/html/2006/article/info8/a_437d8cd212e1b027.htm

struts源代码阅读(Commons-Beanutils包)

站点:爱心种子小博士 关键字:struts源代码阅读(Commons-

   
struts源代码阅读(Commons-Beanutils包)
摘要:
既然是说Struts源代码,为什么要讲Commons-Beanutils包呢?原因很简单,Struts的DynaFormBean就是通过这个包里的相关类实现的。本文对Commons-Beanutils 源代码进行了分析..
既然是说Struts源代码,为什么要讲Commons-Beanutils包呢?原因很简单,Struts的DynaFormBean就是通过这个包里的相关类实现的。同样,留下我的邮箱,方便和大家共同交流。我的邮箱是:[email protected]

Commons-Beanutils(一)

Commons-Beanutils 这个是jakarta commons项目中的一个子项目。这个项目开发的目的是帮助开发者动态的获取/设值Java Bean的属性,同时解决每次都要写getXXX和setXXX的麻烦。

一、XXXConvert
这些类都实现Converter接口,提供把value值转化成为相应XXX类的实现。现在只针对四种类型:数字,时间,Boolean和String。在Converter 接口中只有一个方法convert(Class type, Object value),把value对象转换为type所要求的类。XXXConvert类中这个方法的思路是:
1、如果value==null,并且自己内部有缺省的值那么就返回这个缺省的值。如果没有缺省值,就抛出ConversionException异常。
2、如果value instanceOf XXX类,那么就直接返回value。
3、如果上面的都不行,那么调用new XXX(value.toString())或者XXX.valueOf(value.toString())方法来返回。转化失败时,抛出ConversionException异常。

二、特殊的实现
1、对于ClassConverter类,当进入第三种情形的时候,实际执行的是
ClassLoader classLoader =Thread.currentThread().getContextClassLoader();
   if (classLoader == null) {
       classLoader = ClassConverter.class.getClassLoader();
   }
   return (classLoader.loadClass(value.toString()));


2、对于BooleanConverter类,当进入第三种情形的时候,实际执行的是,根据value.toString()的值:yes,y,true, on, 1 返回true;no,n,false,off,0 返回false。如果这些情形都不符合,并且有缺省值的时候则返回缺省值。否则抛出ConversionException;

三、XXXArrayConverter
这些类继承自AbstractArrayConverter类。 AbstractArrayConverter 实际只实现了一个List parseElements(String svalue)方法。这个方法接受的是{value1, value2,...}格式的字符串,逐个解析出来后,放入一个ArrayList中。它通过StreamTokenizer解析字符串:StreamTokenizer是用来分离input stream中读取的字符串,并且可以根据标记区分不同的内容,比如数字,字符或者注释。XXXArrayConverter由于要转换的是一个数组,所以convert(....)方法的实现过程有所不同。
1、如果value==null,并且自己内部有缺省的值那么就返回这个缺省的值。如果没有缺省值,就抛出ConversionException异常。
2、如果model.getClass() == value.getClass(),那么就直接返回value。
3、如果上面的都不行,那么就通过parseElements(value.toString())生成一个数组,再对数组的元素逐个调用new XXX(list.get(i))或者XXX.valueOf(list.get(i))方法,转换成为数组对元素要求的类型。转化失败时,抛出ConversionException异常。

Commons-Beanutils(二)

一、LocaleConverter 与 BaseLocaleConverter
LocaleConverter继承自 Converter接口,定义了一个新方法convert(Class type, Object value, String pattern)。
抽象类BaseLocaleConverter实现了LocaleConverter接口。它的locPattern属性用来表示这个对象的pattern是否是本地化的格式。patttern 是指把何种格式的时间或者数字值转换成标准值。convert(...)的执行过程是:
1、如果value==null,并且自己内部有缺省的值那么就返回这个缺省的值。如果没有缺省值,就抛出ConversionException异常。
2、根据参数pattern值是否为null,调用parse(Object value, String pattern)方法:如果这个参数不为null那么就使用这个参数的值,否则使用对象预存的pattern值。如果这
样做引起了异常,会首先判断是否能够返回缺省的值,不能则抛出ConversionException异常。
3、parse(Object value, String
pattern)方法的实现被抛至继承了它的类具体实现。这个方法虽然把value值表述为Object类型,但是最后都是通过强制转换,转换成为String类型。也就是说它实际上需要的
是String类型的value。

二、 XXXLocaleConverter
把pattern格式的value转换成标准格式的相应的XXX类。这些类可以分为两大类:一类为时间,一类为数值。
1、时间类最后都会通过SimpleDateFormat类对值进行转换,程序如下:
 if(pattern == null) {      
       pattern = locPattern ? new SimpleDateFormat().toLocalizedPattern() :
                 new SimpleDateFormat().toPattern();
   }
   SimpleDateFormat format = new SimpleDateFormat(pattern, locale);
   if (locPattern) {
       formatter.applyLocalizedPattern(pattern);
   }else {
       formatter.applyPattern(pattern);
   }
   return formatter.parse((String) value);


2、数值类最后都会通过DecimalFormat类对值进行转换,程序如下:
DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(locale);
   if (pattern != null) {
       if (locPattern) {
           formatter.applyLocalizedPattern(pattern);
       } else {
           formatter.applyPattern(pattern);
       }
   }
   return formatter.parse((String) value);

这个转化过程要注意精度的问题。由于Number类是所有的数值类的父类,所以转换完成后要检查最后的结果是否是当前要求的精度:如果大于所要求的精度,则抛出ConversionException异常。

Commons-Beanutils(三)

Dyna开头的类,是专门为DynaFormBean而设计的。

一、DynaBean,DynaClass 与 DynaProperty
DynaBean并不是Java中所定义的Bean,而是一种假的Bean。因为它并不是通过getXXX和setXXX方法,对XXX属性进行取值和设值的。它通过一个实现了DynaClass接口的类,帮助管理其所有的属性的类别,而自己则管理对XXX属性值的设定和获取。在设值的时候会通过与name对应的DynaProperty对象,检查赋值的类别是否正确。
DynaProperty类描述的是DynaBean中所包含的属性的类型。DynaProperty类有三个属性:属性的名称:name,属性的名称;type,属性的类别;contentType,如果DynaProperty描述的是个容器对象(List或者Map),那么这个contentType就代表这个容器内元素的类别。这个类值得关注的地方是writeObject和readObject方法的实现。它会首先判断自己的type是否是一个primitive的类,如果是,则先写入true标志,再写入对应的primitive类的编号;否则写入false标志,再写入type。因为在调用readObject方法时,如果得出的是primitive类型,则type的值为XXX.TYPE而不是XXX.class。
DynaClass 是一个接口,用来管理DynaBean中所有的DynaProperty属性。

二、BasiceDyanBean 与 BasicDynaClass
BasiceDyanBean 实现自DynaBean接口。它包含一个实现了DynaClass接口的类的对象,和一个用来存放值的HashMap。这个HashMap的key与DynaClass中HashMap的key是一一对应的。
BasicDynaClass 实现了DynaClass接口,以DynaProperty的name为key保存所有这些DynaProperty对象。它通过newInstance方法动态生成实现了DynaBean接口的类的对象;注意这个类是可以动态指定的,如果没有,那么就是默认的BasicDynaBean类。动态指定类是通过反射实现的,程序如下:
//dynaBeanClass为任意的实现了DynaBean接口的类,constructorTypes为这个
//类的构造方法所需要的参数的类型
constructor = dynaBeanClass.getConstructor(constructorTypes);
//constructorValues为构造方法的参数值,实际上它的值为当前的BasicDynaClass
return ((DynaBean) constructor.newInstance(constructorValues));

Commons-Beanutils(四)

一、ConvertUtils 和 ConvertUtilsBean
ConvertUtils 是ConvertUtilsBean类的一个简单封装,即ConvertUtils中的所有方法都是通过直接调用ConvertUtilsBean中的同名方法实现的。如果你需要更复杂的功能,就使用ConvertUtilsBean,否则使用ConvertUtils。
ConvertUtilsBean 通过一个HashMap管理所有的XXXConverter。这个HashMap的key为XXX的类全名,值为相应的XXXConverter对象。通过deregister()方法,初始化这个HashMap。这个初始化方法会为每一个XXXConverter类提供一个缺省的值。用户可以动过setDefaultXXX(...)方法来自行设置XXXConverter对象的缺省值。这个类还提供了convert(...)方法,对String value进行相应的转化。

二、PropertyUtils 和 PropertyUtilsBean
PropertyUtils 是PropertyUtilsBean类的一个简单封装,同样它的所有方法都是通过直接调用PropertyUtilsBean 中同名方法实现的。
PropertyUtilsBean 对DynaBean或者一个java标准Bean中的属性动态的赋值和取值(非通过getXXX和setXXX方法)。
1、这个类支持多层嵌套,比如:XXX[i].YYY(key).ZZZ,那么它会为你得到或者设置ZZZ的属性。
2、所有的set/get方法介绍:
//对XXX(key)格式的name设值
setMappedProperty(Object bean, String name,String key, Object value)
//对XXX[i]格式的name设值
setIndexedProperty(Object bean, String name, int index, Object value)
//对XXX格式的name设值
setSimpleProperty(Object bean, String name, Object value)
//对XXX(key).YYY[i].ZZZ格式的名称设值。注意,name必须要遵照这种格式。
//这个方法实际做的就是以.为分隔符,逐层的根据情况分别调用上面的几个方法,
//获取相应的bean。
setNestedProperty(Object bean, String name, Object value)
//它直接调用setNestedProperty方法
setProperty(Object bean, String name, Object value)
3、getPropertyType(Object bean, String name)方法中用来获取Bean的某一个property的类型的代码:
PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
if (descriptor == null) {
return (null);
}else if (descriptor instanceof IndexedPropertyDescriptor) {
return (((IndexedPropertyDescriptor) descriptor).getIndexedPropertyType());
} else if (descriptor instanceof MappedPropertyDescriptor) {
return (((MappedPropertyDescriptor) descriptor).getMappedPropertyType());
} else {
return (descriptor.getPropertyType());
}
4、getIndexedProperty(Object bean, String name, int index)
这个方法用来获取一个数组或者一个List中的属性。它会首先看这个bean是否是DynaBean类型的,如果是,再其检查是否有name这个属性,如果有那么就直接调用get(String name, int index)方法返回值;如果不是DynaBean类型,那么就会执行如下方法:
//有没有为数组的某个特定元素取值的方法
if (descriptor instanceof IndexedPropertyDescriptor) {
Method readMethod = ((IndexedPropertyDescriptor) descriptor).
getIndexedReadMethod();
if (readMethod != null) {
Object subscript[] = new Object[1];
subscript[0] = new Integer(index);
return (invokeMethod(readMethod,bean, subscript));
}
}
// 如果没有,就先取出整个对象
Method readMethod = getReadMethod(descriptor);
if (readMethod == null) {
throw new NoSuchMethodException("Property " + name +
" has no getter method");
}
Object value = invokeMethod(readMethod, bean, new Object[0]);
//如果这个对象实际上是一个List,那么调用get()方法
if (!value.getClass().isArray()) {
if (!(value instanceof java.util.List)) {
throw new IllegalArgumentException("Property " + name
+ " is not indexed");
} else {
//get the Lists value
return ((java.util.List) value).get(index);
}
//否则通过Array类提供的相应方法取值
} else {
//get the arrays value
return (Array.get(value, index));
}

三、BeanUtil 和 BeanUtilBean
BeanUtils 是BeanUtilsBean类的一个简单封装,同样它的所有方法都是通过直接调用BeanUtilsBean 中同名方法实现的。
BeanUtilBean中大多数核心方法都是通过调用PropertyUtilsBean中的方法实现的。而populate(Object bean, Map properties)是自己实现的,因为这个赋值过程要首先对value进行格式的转化;这个方法把properties中的key为属性名,value为属性的值,分别对应的设值给bean对象。它通过setProperty(Object bean, String name, Object value)方法实现逐个设值的。由于此时的value不一定符合bean中name属性的类型,所以首先要把value转换成合适的值,然后再设值。具体的类型转换方法如下:

//这种类型转换的原则是:如果value是String或者String[],那么这个值可能为任意的类型, 
    //需要进行转换。如果为其它的类型,则不进行任何转换。
    if (type.isArray() && (index < 0)) {
        // 如果是直接对一个数组赋值,则使用convert(String values[], Class clazz)方法转换
        if (value == null) {
            String values[] = new String[1];
            values[0] = (String) value;
            newValue = getConvertUtils().convert((String[]) values, type);
        } else if (value instanceof String) {
            String values[] = new String[1];
            values[0] = (String) value;
            newValue = getConvertUtils().convert((String[]) values, type);
        } else if (value instanceof String[]) {
            newValue = getConvertUtils().convert((String[]) value, type);
        } else {
            newValue = value;
        }
    } else if (type.isArray()) {
        // 如果是对数组的某一个元素赋值,则使用convert(String value, Class clazz)方法转换
        if (value instanceof String) {
            newValue = getConvertUtils().convert((String) value, type.getComponentType());
        } else if (value instanceof String[]) {   
            newValue = getConvertUtils().convert(((String[]) value)[0],type.getComponentType());
        } else {
            newValue = value;     
        }
    } else {                 
        // 否则就是一对一的简单赋值,则使用convert(String value, Class clazz)方法转换
        if ((value instanceof String) || (value == null)) {
            newValue = getConvertUtils().convert((String) value, type);
        } else if (value instanceof String[]) {
            newValue = getConvertUtils().convert(((String[]) value)[0], type);       
        } else if (getConvertUtils().lookup(value.getClass()) != null) {
            newValue = getConvertUtils().convert(value.toString(), type);
        } else {
            newValue = value;        }
    }