commons-beanutil开源库是apache组织的一个基础的开源库,为apache中许多类提供工具方法,学习它是学习其余开源库实现的基础。java
Commons-beanutil中包含大量和JavaBean操做有关的工具方法,使用它能够轻松利用Java反射机制来完成代码中所须要的功能,而不须要详细研究反射的原理和使用,同时,该类库中提出了动态Bean的概念,不但提供现有JavaBean的全部功能,并且还能够在运行时动态的对Bean中的属性数据类型进行修改以及增删属性。web
本文研究的是v1.7版本的commons-utils类库。算法
转换器用来将输入数据转换成须要的数据类型,同时提供统一的接口,方便客户代码使用和扩展。sql
Commons-beanutils包中,全部转换器都从org.apache.commons.beanutils.Converter接口集成,添加本身须要的实现。apache
转换器分为如下三个部分:编程
l 数组转换器数组
l 普通转换器架构
l 地区敏感的转换器app
l 转换器工具类框架
Converter子类包含的都是转换器的实现,通常状况下,不须要直接实例化这些类,只须要使用ConvertUtil中convert方法,就能够进行数据类型的转换。高级用户不但可使用默认的转换方式,还能够向ConvertUtils中注册新的或替代原有的转换器,实现须要的业务逻辑。
转换器接口的详细信息以下:
类名 |
描述 |
Converter |
BeanUtil框架中使用的类型转换接口,能够将输入数据转换成须要的类型 |
数组转换器的实现被封装在org.apache.commons.beanutils.converters包中。它的功能是将必定格式的输入字符串转换成不一样类型的数组,输入数据以逗号分隔,开头和结尾能够用大括号括起来,例如:“{1, 2, 3, 4, 5}”。
全部数组转换器实现都从一个名为AbstractArrayConverter的抽象基类中集成,这个类提供了解析输入字符串的工具方法。
数组转换器的详细类说明以下:
类名 |
描述 |
AbstractArrayConverter |
用来将输入字符串转换成数组的抽象类,提供了全部ArrayConverter须要的公共方法。 |
BooleanArrayConverter |
将输入的任何对象转换成boolean数组,传入对象要知足如下几个条件后才能正确转换: 1. 传入对象为boolean数组,直接返回。 2. 传入对象为String数组,只要数组中的每一个元素知足特定条件,就能够正常解析为boolean数组。 3. 传入对象为其余类型,只要对象的toString()方法返回的字符串为逗号分隔的格式,而且每部分知足特定条件,就能够解析为boolean数组。 能够向boolean类型转换的字符串以下: l yes,y,true,on,1被转换为true l no,n,false,off,被转换成false l 其余字符串为非法字符串,若是遇到就中止转换,抛出异常或返回默认值。 原有代码实现的缺陷和改进方案: 1. 字符串数组解析算法重复:能够经过提取公共函数的方法消除重复。 2. try/catch嵌套混乱:解决方法同上,只要提取公共方法后天然就能够解决这个问题。 3. 特殊字符串被硬编码,例如yes,y,no,n等:将这些特殊字串提取成常量,放入映射表中维护,减小复杂的判断语句。 |
ByteArrayConverter |
将传入对象转换为byte数组,若是转换失败,就抛出异常。 仍然有重复代码的问题。 |
CharacterArrayConverter |
将对象转换为char数组 |
DoubleArrayConverter |
将对象转换成double数组 |
FloatArrayConverter |
将对象转换成float数组 |
IntegerArrayConverter |
将对象转换成int数组 |
LongArrayConverter |
将对象转换成long数组 |
ShortArrayConverter |
将对象转换成short数组 |
StringArrayConverter |
javadoc中说是将String数组转换成String数组,但不知道这样有什么意义。 查看代码后发现算法只能转换int型的数组为String数组,其余的类型好比long型数组均不能正常转换。 最好不用这个类。 |
以上类构成了以下的类结构图:
经过阅读数组转换器的代码,发现代码存在如下问题:
1. 代码冗余:代码不够简洁,每一个类中都或多或少的存在代码复制粘贴的痕迹。
2. 部分类的类型转换时存在缺陷,不能正常转换。
普通转换器提供了将字符串转换成Java中的数字、时间日期类型和其余类型对象的方法。
普通转换器都直接从Converter接口集成,实现其中的抽象方法。
用户不但能够直接使用这些工具方法,也能够本身实现一些特殊业务需求的转换器,只要实现Converter接口便可。
普通转换器的类说明以下:
类名 |
描述 |
BigDecimalConverter |
将字符串转换成BigDecimal类型数据。转换失败时能够抛出异常,也能够返回默认值。 |
BigIntegerConverter |
将数组转换成java.math.BigInteger类型对象,若是转换失败,能够抛出异常,也能够直接返回默认值。 |
BooleanConverter |
将字符串转换成boolean类型对象。 若是转换失败,能够抛出异常,也能够返回默认值。 |
ByteConverter |
将字符串转换成byte类型,若是转换失败,抛出异常或返回默认值。 |
CharacterConverter |
将字符串转换成char,若是转换失败,抛出异常或返回默认值。 |
ClassConverter |
从当前上下文的ClassLoader中加载类,若是类不存在,能够抛出异常,也能够直接返回默认值。 |
DoubleConverter |
将输入字符串转换成double类型。若是转换失败,能够抛出异常,也能够返回默认值。 |
FileConverter |
根据输入字符串初始化File对象,若是对象建立失败,抛出异常或返回默认值。 |
FloatConverter |
将字符串转换成Float类型,若是转换失败,能够抛出异常,能够返回默认值。 |
IntegerConverter |
将字符串转换成Integer类型对象,若是转换失败,能够抛出异常,也能够返回默认值。 |
LongConverter |
将字符串转换成Long类型对象,若是转换失败,能够抛出异常,也能够返回默认值。 |
ShortConverter |
将字符串转换成short类型对象,若是转换失败,能够抛出异常,也能够返回默认值。 |
SqlTimeConverter |
将字符串转换成java.sql.Time对象,若是转换失败,能够抛出异常,也能够返回默认值。 |
SqlTimestampConverter |
将字符串转换成javax.sql.Timestamp对象,若是转换失败,能够抛出异常,也能够返回默认值。 |
StringConverter |
将字符串对象转换成字符串对象。 单独使用没有什么意义,可是在面向接口编程中实现了一种通用的转换方法,比较有用。 |
URLConverter |
将字符串转换成URL对象,若是转换失败,抛出异常,或者直接返回默认值。 |
|
|
通用转换器的类结构图以下:
通用转换器存在的问题是:
对于默认值的校验不到位,没有针对具体Converter的类型进行校验,一旦转换失败,直接返回默认值后可能致使后续代码出现ClassCastException,没有从源头杜绝这种状况发生,虽然这样设计更加灵活,但弊大于利,最好是在类建立时进行校验。
地区敏感转换器都被封装在org.apache.commons.beanutils.locale和org.apache.commons.beanutils.locale.converters包中,前者提供一个集成自Converter的通用接口,后者提供这个接口的具体实现。
地区敏感转换器主要实现了当须要分地区进行转换时,须要进行的操做。主要功能是将带有不一样地区特征的字符串转换成数字和时间日期类型对象。
地区敏感转换器的类说明以下:
类名 |
描述 |
LocaleConverter |
进行地区敏感的数据类型的转换 |
BaseLocaleConverter |
封装全部地区敏感conveter的公共方法 |
DateLocaleConverter |
将地区敏感对象转换成java.util.Date对象。 |
SqlDateLocaleConverter |
将输入对象转换成java.sql.Date 对象 |
SqlTimeLocaleConverter |
将输入对象转换成java.sql.Time对象 |
SqlTimestampLocaleConverter |
将输入对象转换成java.sql.Timestamp的格式 |
DecimalLocaleConverter |
将地区敏感的输入转换成java.lang.Decimal对象 |
BigDecimalLocaleConverter |
将输入的地区敏感字符串转换成java.math.BigDecimal对象。 没有重写任何方法,应该只是为了和其余实现样式一致编写的方法。 |
BigIntegerLocaleConverter |
将输入的地区敏感的对象转换成java.math.BigInteger 对象 没有重写任何方法,应该只是为了和其余实现样式一致编写的方法。 |
ByteLocaleConverter |
将输入的地区敏感的字符串转换成java.lang.Byte 对象 |
DoubleLocaleConverter |
将地区敏感的对象转换成java.lang.Double对象 |
FloatLocaleConverter |
将地区敏感的字符串转换成java.lang.Float对象 |
IntegerLocaleConverter |
将地区敏感的字符串转换成java.lang.Integer对象 |
LongLocaleConverter |
将地区敏感的字符串转换成java.lang.Long对象 |
ShortLocaleConverter |
将地区敏感的字符串转换成java.lang.Short对象 |
StringLocaleConverter |
将字符串转换成数字的字符串形式。 好像没有什么实际的做用 |
地区敏感转换器的类图以下:
地区敏感转换器中全部参数参数都是直接从构造函数中输入的,没有get和set,代码冗余度很大,须要重构。
转换器相关的工具类是外界实际使用的接口,默认状况下,系统会向工具类中注册上述各类类型数据的转换器对象,用户能够自定义这些注册信息,添加,修改或删除本身不须要的转换器,还能够将本身实现的类型注册到转换器中。
转换器工具类的详细信息以下:
类名 |
描述 |
ConvertUtils |
将字符串对象转换成相应类型的对象。如,将String对象转换成Integer类型的对象,或将String对象转换成Integer数组对象。 默认使用系统自定义的转换器,但接口开放,能够自定义转换器进行数据类型转换。 |
ConvertUtilsBean |
实际进行数据转换的类。 |
LocaleConvertUtils |
和ConvertUtils做用相似,在转换的过程当中根据不一样的地区进行不用的转换,适用于地区敏感的数据。 |
LocaleConvertUtilsBean |
转换器这一套代码中实现了字符串向Java中各类数据类型的转换,这样在转换数据类型时不须要了解各类数据类型的转换API,只须要知道Converter接口便可,方便了开发人员编写代码。
但这套代码也有不少不足,其中最大的就是代码冗余的问题,小到函数内部的实现,大到整个的类结构,或多或少的都存在代码复制粘贴的影子,能够经过重构让代码变得更加清晰。
咱们知道,每个JavaBean对象中包含一个Class对象,这个对象是单实例的而且在当前类加载器中全局惟一,由JVM维护,用来存储Bean中的属性描述信息,这些信息在运行时没法修改。
动态bean符合JavaBean架构的基本思想,每个DynaBean实例有一个DynaClass对象,这个对象在同类DynaBean中惟一,但能够动态的对属性进行增删改的操做。这样就弥补了原来JavaBean架构中当Bean定义后不能对Bean中属性进行扩展的缺点,同时,提供了对bean中属性进行get/set的统一工具类,这些工具类的接口能够兼容动态Bean、标准Bean,以及映射(map)。
动态Bean中的属性使用DynaProperty对象进行描述,
类名 |
描述 |
DynaProperty |
动态bean中的属性,由属性名,属性类型两部分组成,对于数组、链表这类复杂类型,还加入了内容类型的概念,用来描述这些复杂数据接口内部对象的类型。 |
动态Bean的Class对象描述了Bean中包含的属性以及属性的数据类型,分为DynaClass和MutableDynaClass两个接口,其中MutableDynaClass接口继承自DynaClass接口,同时提供对Bean属性进行修改的方法。
详细描述以下:
接口名 |
描述 |
DynaClass |
动态类模仿java.lang.Class 的实现。使用DynaClass建立DynaBean 对象,全部DynaBean 对象共享一个DynaClass实例。 |
MutableDynaClass |
对于DynaClass的特殊扩展,容许动态的添加和移除类的属性 |
有多个类扩展了以上两个接口,详细信息以下:
类名 |
描述 |
BasicDynaClass |
对DynaClass接口的基本实现,提供了最基本的功能。 |
JDBCDynaClass |
实现JDBC逻辑的动态类 |
ResultSetDynaClass |
封装java.sql.ResultSet对象,提供和其余对象同样访问方式的类。 |
RowSetDynaClass |
从ResultSet中读取全部数据,封装在RowSetDynaBean中。 |
LazyDynaClass |
|
DynaProperty |
动态bean中的属性 |
WrapDynaClass |
封装标准JavaBean的动态bean的DynaClass对象 |
上述类之间的关系以下:
全部动态Bean实例都继承自DynaBean接口,能够建立DynaBean的实例,并对其属性进行修改。
接口详细信息以下:
接口名 |
描述 |
DynaBean |
提供了属性类型,名称,内容能够动态修改的JavaBean。 |
如下类实现了DynaBean接口:
类名 |
描述 |
WrapDynaClass |
封装标准JavaBean的动态bean的DynaClass对象 |
BasicDynaBean |
对DynaBean接口的最小实现 |
ResultSetIterator |
封装ResultSetDynaClass的DynaBean |
LazyDynaBean |
能够动态添加属性的Bean |
WrapDynaBean |
封装标准的JavaBean,提供DynaBean的访问方式 |
ConvertingWrapDynaBean |
WrapDynaBean的子类,在set数据时能够提供必要的数据类型转换 |
DynaBean相关类之间关系以下:
和任何成熟的开源包同样,commons-beanutils做为一个工具包,提供了对JavaBean以及动态Bean进行操做的工具类,即便没有使用动态Bean,仍然能够放心的使用这些工具类。
经常使用的工具类有如下几个部分:
l PropertyUtils:对JavaBean中的属性值进行操做。
l MethodUtils:使用反射的方式请求bean中的方法。
l ConstructorUtils:使用反射的方式构造Bean的新实例。
l BeanUtils:对JavaBean提供拷贝,赋值等操做。
l LocaleBeanUtils:和BeanUtils功能相似,但还能够提供地区敏感数据的操做。
l ContextClassLoaderLocal:为不一样线程保存须要数据的工具类。
工具类的详细说明以下:
类名 |
描述 |
ContextClassLoaderLocal |
提供保存不一样线程数据的工具类,在JDK1.5中已经能够用ThreadLocal代替. |
PropertyUtils |
使用Java反射API编写的工具类,用于方便的进行get和set。 本工具类的全部实现都是对PropertyUtilsBean的封装和代理。 |
PropertyUtilsBean |
使用Java反射API进行set和get的工具类。 |
MethodUtils |
封装以放射方式请求方法的工具方法 |
ConstructorUtils |
使用反射方法请求构造函数建立新实例的工具类,能够简化程序中使用反射方式建立对象的代码。 |
BeanUtilsBean |
提供对标准JavaBean和动态bean的操做。主要功能是复制bean中的内容,拷贝bean,为bean中的内容赋值和读取bean中内容。 |
BeanUtils |
|
LocaleBeanUtils |
和BeanUtils做用相似,但在执行相应方法时能够进行地区敏感数据的转换。 |
LocaleBeanUtilsBean |
经过研读commons-beanutils的源代码,整理了PropertyUtilsBean中的相关方法,以下所示:
PropertyUtilsBean的方法名 |
描述 |
copyProperties |
bean属性拷贝(copyProperties),能够拷贝bean中全部属性,拷贝时遵循原来bean中的访问控制策略: l 动态bean向动态bean拷贝 l 动态bean向标准bean拷贝 l MAP向动态bean拷贝 l Map向标准bean拷贝 l 标准bean向动态bean拷贝 l 标准bean向标准bean拷贝 |
describe |
将bean属性拷贝到Map中。 只拷贝源bean中可读的属性,忽略其余属性。 |
getIndexedProperty |
获得bean中的索引属性值: 有两种形式,一种的参数是string,另外一种的参数是属性名和位置,前者是“name[1]”的形式,后者是“name, 1”的形式。 例如,要取出bean中名为name属性的第2个对象,可使用getIndexedProperty(bean, “name[1]”)的形式,也可使用getIndexedProperty(bean, “name”, 1)的形式。 l 若是输入是动态bean,能够获得动态bean的索引属性。 若是属性是数组或列表,能够获得相应属性。 |
getMappedProperty |
获得bean中的映射属性值: 本方法有两种原型,能够输入(bean, “name(key)”)取出bean中名为name映射属性中以key为键的属性值;也能够输入(bean, “name”, “key”)的方式取出bean中名为name映射属性中以key为键的属性值。
|
getNestedProperty |
获得bean中的嵌套属性值。获取值的bean须要有get方法,还要有public访问权限,不然BeanUtils中的类没法访问。 适合在web页面上进行bean值的读取。 |
getPropertyDescriptor |
获得bean中相应属性的属性描述符 |
getPropertyDescriptors |
获得bean中全部属性的属性描述符 |
getPropertyEditorClass |
获得bean中的属性编辑器类 |
getPropertyType |
获得bean中相应属性类型 |
getReadMethod |
获得属性描述符中的get方法 |
getSimpleProperty |
获得bean中简单属性的值 |
getWriteMethod |
获得属性描述符中的写方法 |
isReadable |
判断bean中的指定属性是否可读 |
isWriteable |
判断bean中的对应方法是否可写 |
setIndexedProperty |
向bean中的索引属性赋值 |
setMappedProperty |
向bean中的映射属性赋值 |
setNestedProperty |
向bean中的内嵌属性赋值 |
setProperty |
为bean中的属性赋值(包括简单属性和索引属性) |
setSimpleProperty |
为bean中的简单属性赋值 |
Commons-beanutils是一款优秀的工具类库。不但提供了一种能够动态扩展属性的JavaBean,同时封装了Java的反射机制,使用者能够更加容易的对反射进行操做,而不须要了解那么多和反射相关的知识。