既然是说Struts源代码,Z么要讲Commons-Beanutils包呢Q原因很单,Struts的DynaFormBean是通过q个包里的相关类实现的。同P留下我的邮箱Q方便和大家共同交流。我的邮是Qmariah_fan@hotmail.com?BR>
Commons-BeanutilsQ一Q?Commons-Beanutils q个是jakarta commons目中的一个子目。这个项目开发的目的是帮助开发者动态的获取/讑րJava Bean的属性,同时解决每次都要写getXXX和setXXX的麻烦?BR>
一、XXXConvertq些c都实现Converter接口Q提供把valueD{化成为相应XXXcȝ实现。现在只针对四种cdQ数字,旉QBoolean和String。在Converter 接口中只有一个方法convert(Class type, Object value)Q把value对象转换为type所要求的类。XXXConvertcMq个Ҏ的思\是:
1、如果value==nullQƈ且自己内部有~省的值那么就q回q个~省的倹{如果没有缺省|抛出ConversionException异常?BR>2、如果value instanceOf XXXc,那么q接返回value?BR>3、如果上面的都不行,那么调用new XXX(value.toString())或者XXX.valueOf(value.toString())Ҏ来返回。{化失败时Q抛出ConversionException异常?BR>
二、特D的实现1、对于ClassConverterc,当进入第三种情Ş的时候,实际执行的是
ClassLoader classLoader =Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = ClassConverter.class.getClassLoader();
}
return (classLoader.loadClass(value.toString()));
2、对于BooleanConverterc,当进入第三种情Ş的时候,实际执行的是Q根据value.toString()的|yesQyQtrue, on, 1 q回trueQnoQnQfalseQoffQ? q回false。如果这些情形都不符合,q且有缺省值的时候则q回~省倹{否则抛出ConversionExceptionQ?BR>
三、XXXArrayConverterq些cȝ承自AbstractArrayConvertercR?AbstractArrayConverter 实际只实C一个List parseElements(String svalue)Ҏ。这个方法接受的是{value1, value2,...}格式的字W串Q逐个解析出来后,攑օ一个ArrayList中。它通过StreamTokenizer解析字符ԌStreamTokenizer是用来分input stream中读取的字符Ԍq且可以Ҏ标记区分不同的内容,比如数字Q字W或者注释。XXXArrayConverter׃要{换的是一个数l,所以convert(....)Ҏ的实现过E有所不同?BR>1、如果value==nullQƈ且自己内部有~省的值那么就q回q个~省的倹{如果没有缺省|抛出ConversionException异常?BR>2、如果model.getClass() == value.getClass()Q那么就直接q回value?BR>3、如果上面的都不行,那么通过parseElements(value.toString())生成一个数l,再对数组的元素逐个调用new XXX(list.get(i))或者XXX.valueOf(list.get(i))ҎQ{换成为数l对元素要求的类型。{化失败时Q抛出ConversionException异常?BR>
Commons-BeanutilsQ二Q?一、LocaleConverter ?BaseLocaleConverterLocaleConverterl承?Converter接口Q定义了一个新Ҏconvert(Class type, Object value, String pattern)?BR>抽象cBaseLocaleConverter实现了LocaleConverter接口。它的locPattern属性用来表C个对象的pattern是否是本地化的格式。patttern 是指把何U格式的旉或者数字D{换成标准倹{convert(...)的执行过E是Q?BR>1、如果value==nullQƈ且自己内部有~省的值那么就q回q个~省的倹{如果没有缺省|抛出ConversionException异常?BR>2、根据参数pattern值是否ؓnullQ调用parse(Object value, String pattern)ҎQ如果这个参C为null那么׃用这个参数的|否则使用对象预存的pattern倹{如果这
样做引v了异常,会首先判断是否能够返回缺省的|不能则抛出ConversionException异常?BR>3、parse(Object value, String
pattern)Ҏ的实现被抛至l承了它的类具体实现。这个方法虽然把valueDqCؓObjectcdQ但是最后都是通过强制转换Q{换成为Stringcd。也是说它实际上需要的
是Stringcd的value?BR>
二?XXXLocaleConverter把pattern格式的value转换成标准格式的相应的XXXcR这些类可以分ؓ两大c:一cMؓ旉Q一cMؓ数倹{?BR>1、时间类最后都会通过SimpleDateFormatcdD行{换,E序如下Q?BR>
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、数值类最后都会通过DecimalFormatcdD行{换,E序如下Q?BR>
DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(locale);
if (pattern != null) {
if (locPattern) {
formatter.applyLocalizedPattern(pattern);
} else {
formatter.applyPattern(pattern);
}
}
return formatter.parse((String) value);
q个转化q程要注意精度的问题。由于NumbercL所有的数值类的父c,所以{换完成后要检查最后的l果是否是当前要求的_ֺQ如果大于所要求的精度,则抛出ConversionException异常?BR>
Commons-BeanutilsQ三Q?/B>
Dyna开头的c,是专门ؓDynaFormBean而设计的?BR>
一、DynaBeanQDynaClass ?DynaProperty
DynaBeanq不是Java中所定义的BeanQ而是一U“假”的Bean。因为它q不是通过getXXX和setXXXҎQ对XXX属性进行取值和讑ր的。它通过一个实CDynaClass接口的类Q帮助管理其所有的属性的cdQ而自己则理对XXX属性值的讑֮和获取。在讑ր的时候会通过与name对应的DynaProperty对象Q检查赋值的cd是否正确?BR>DynaPropertycLq的是DynaBean中所包含的属性的cd。DynaPropertycL三个属性:属性的名称QnameQ属性的名称QtypeQ属性的cdQcontentTypeQ如果DynaProperty描述的是个容器对?List或者Map)Q那么这个contentType׃表这个容器内元素的类别。这个类值得x的地ҎwriteObject和readObjectҎ的实现。它会首先判断自qtype是否是一个primitive的类Q如果是Q则先写入true标志Q再写入对应的primitivecȝ~号Q否则写入false标志Q再写入type。因为在调用readObjectҎӞ如果得出的是primitivecdQ则type的gؓXXX.TYPE而不是XXX.class?BR>DynaClass 是一个接口,用来理DynaBean中所有的DynaProperty属性?BR>
二、BasiceDyanBean ?BasicDynaClass
BasiceDyanBean 实现自DynaBean接口。它包含一个实CDynaClass接口的类的对象,和一个用来存攑ր的HashMap。这个HashMap的key与DynaClass中HashMap的key是一一对应的?BR>BasicDynaClass 实现了DynaClass接口Q以DynaProperty的name为key保存所有这些DynaProperty对象。它通过newInstanceҎ动态生成实CDynaBean接口的类的对象;注意q个cL可以动态指定的Q如果没有,那么是默认的BasicDynaBeancR动态指定类是通过反射实现的,E序如下Q?BR>//dynaBeanClassZQ意的实现了DynaBean接口的类QconstructorTypes?BR>//cȝ构造方法所需要的参数的类?BR>constructor = dynaBeanClass.getConstructor(constructorTypes);
//constructorValues为构造方法的参数|实际上它的gؓ当前的BasicDynaClass
return ((DynaBean) constructor.newInstance(constructorValues));
Commons-BeanutilsQ四Q?/B>
一、ConvertUtils ?ConvertUtilsBean
ConvertUtils 是ConvertUtilsBeancȝ一个简单封装,即ConvertUtils中的所有方法都是通过直接调用ConvertUtilsBean中的同名Ҏ实现的。如果你需要更复杂的功能,׃用ConvertUtilsBeanQ否则用ConvertUtils?BR>ConvertUtilsBean 通过一个HashMap理所有的XXXConverter。这个HashMap的key为XXX的类全名Qgؓ相应的XXXConverter对象。通过deregister()ҎQ初始化q个HashMap。这个初始化Ҏ会ؓ每一个XXXConvertercL供一个缺省的倹{用户可以动qsetDefaultXXX(...)Ҏ来自行设|XXXConverter对象的缺省倹{这个类q提供了convert(...)ҎQ对String valueq行相应的{化?BR>
二、PropertyUtils ?PropertyUtilsBean
PropertyUtils 是PropertyUtilsBeancȝ一个简单封装,同样它的所有方法都是通过直接调用PropertyUtilsBean 中同名方法实现的?BR>PropertyUtilsBean 对DynaBean或者一个java标准Bean中的属性动态的赋值和取?非通过getXXX和setXXXҎ)?BR>1、这个类支持多层嵌套Q比如:XXX[i].YYY(key).ZZZQ那么它会ؓ你得到或者设|ZZZ的属性?BR>2、所有的set/getҎ介绍Q?BR>//对XXX(key)格式的name讑ր?BR>setMappedProperty(Object bean, String name,String key, Object value)
//对XXX[i]格式的name讑ր?BR>setIndexedProperty(Object bean, String name, int index, Object value)
//对XXX格式的name讑ր?BR>setSimpleProperty(Object bean, String name, Object value)
//对XXX(key).YYY[i].ZZZ格式的名U设倹{注意,name必须要遵照这U格式?BR>//q个Ҏ实际做的是以?”ؓ分隔W,逐层的根据情况分别调用上面的几个ҎQ?BR>//获取相应的bean?BR>setNestedProperty(Object bean, String name, Object value)
//它直接调用setNestedPropertyҎ
setProperty(Object bean, String name, Object value)
3、getPropertyType(Object bean, String name)Ҏ中用来获取Bean的某一个property的类型的代码Q?BR>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)
q个Ҏ用来获取一个数l或者一个List中的属性。它会首先看q个bean是否是DynaBeancd的,如果是,再其查是否有nameq个属性,如果有那么就直接调用get(String name, int index)Ҏq回|如果不是DynaBeancdQ那么就会执行如下方法:
//有没有ؓ数组的某个特定元素取值的Ҏ
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));
}
}
// 如果没有Q就先取出整个对?BR>Method readMethod = getReadMethod(descriptor);
if (readMethod == null) {
throw new NoSuchMethodException("Property '" + name +
"' has no getter method");
}
Object value = invokeMethod(readMethod, bean, new Object[0]);
//如果q个对象实际上是一个ListQ那么调用get()Ҏ
if (!value.getClass().isArray()) {
if (!(value instanceof java.util.List)) {
throw new IllegalArgumentException("Property '" + name
+ "' is not indexed");
} else {
//get the List's value
return ((java.util.List) value).get(index);
}
//否则通过ArraycL供的相应Ҏ取?BR>} else {
//get the array's value
return (Array.get(value, index));
}
三、BeanUtil ?BeanUtilBean
BeanUtils 是BeanUtilsBeancȝ一个简单封装,同样它的所有方法都是通过直接调用BeanUtilsBean 中同名方法实现的?BR>BeanUtilBean中大多数核心Ҏ都是通过调用PropertyUtilsBean中的Ҏ实现的。而populate(Object bean, Map properties)是自己实现的Q因个赋DE要首先对valueq行格式的{化;q个Ҏ把properties中的key为属性名Qvalue为属性的|分别对应的设值给bean对象。它通过setProperty(Object bean, String name, Object value)Ҏ实现逐个讑ր的。由于此时的value不一定符合bean中name属性的cdQ所以首先要把value转换成合适的|然后再设倹{具体的cd转换Ҏ如下Q?BR>
//q种cd转换的原则是Q如果value是String或者String[]Q那么这个值可能ؓL的类型,
//需要进行{换。如果ؓ其它的类型,则不q行M转换?BR> if (type.isArray() && (index < 0)) {
// 如果是直接对一个数l赋|则用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; }
}