当前页面位置: » 丰搜网 » 文档中心 » 详细内容
struts源代码阅读(commons-beanutils包)
既然是说struts源代码,为什么要讲commons-beanutils包呢?原因很简单,struts的dynaformbean就是通过这个包里的相关类实现的。同样,留下我的邮箱,方便和大家共同交流。我的邮箱是:mariah_fan@hotmail.com。
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 list's value
return ((
java.util.list) value).get(index);
}
//否则通过array类提供的相应方法取值
} else {
//get the array's 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; }
}