------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
1 JavaBean
JavaBean 是一种特殊的Java 类,主要用于传递数据信息,这种java 类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean 中,这种JavaBean 的实例对象通常称之为值对象(Value Object,简称VO)。
这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,JavaBean 的属性是根据其中的setter 和getter 方法来确定的,而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,如果方法名为getId,中文意思即为获取id。去掉set 前缀,剩余部分就是属性名。
2 内省综合案例
2.1 PropertyDescriptor类
(1)getPropertyType(),获得属性的Class对象。
(2)getReadMethod(),获得用于读取属性值的方法 getWriteMethod(),获得用于写入属性值的方法。
(3)setReadMethod(Method readMethod),设置用于读取属性值的方法
setWriteMethod(MethodwriteMethod),设置用于写入属性值的方法
右键—》Source—》Generate Geters and Setters,创建get()和set()方法。
选择一些代码,右键—》Refactor—》Extract Method,创建一个方法,提高复用性。
public class ReflectPoint {
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public ReflectPoint(int x, int y) {
this.x = x;
this.y = y;
}
public void setY(int y) {
this.y = y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
public class IntroSpectorTest {
public static void main(String[] args) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
ReflectPoint rp = new ReflectPoint(3, 4);
String propertyName = "x";
getProperty(rp, propertyName);
int value = 9;
setProperty(rp, propertyName, value);
System.out.println(rp.getX());
}
// 设置属性值的方法
private static void setProperty(ReflectPoint rp, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
// 创建属性描述符对象,将属性名称和加载文件等信息写入其中
PropertyDescriptor pd = new PropertyDescriptor(propertyName,
rp.getClass());
// 通过反射的方法类Method,获取属性所对应的set 方法
Method mSexX = pd.getWriteMethod();
mSexX.invoke(rp, value);
}
// 获取属性值的方法
private static Object getProperty(Object rp, String propertyName)
throws IntrospectionException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName,
rp.getClass());
Method mGetX = pd.getReadMethod();
Object retVal = mGetX.invoke(rp);
return retVal;
}
}
2.2 Introspector类
通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
-->得到BeanInfo 最好采用“obj.getClass()”方式,而不要采用“类名.class”方式,这样程序更通用。
-->采用遍历BeanInfo 的所有属性方式来查找和设置某个RefectPoint 对象的x 属性。在程序中把一个类当作JavaBean 来看,就是调用IntroSpector.getBeanInfo 方法,得到的BeanInfo对象封装了把这个类当作JavaBean 看的结果信息。
// 获取属性值的方法
public static Object getProperty(Object rp, String propertyName)
throws IntrospectionException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
BeanInfo bi = Introspector.getBeanInfo(rp.getClass());
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
Object retVal = null;
for (PropertyDescriptor pd : pds) {
if (propertyName.equals(pd.getName())) {
Method method = pd.getReadMethod();
method.invoke(rp);
break;
}
}
return retVal;
}
2.3 beanutils工具包
需要jar包:commons-beanutils.jar,commons-logging.jar
BeanUtils的特点:
1、对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成。
2、对引用数据类型的属性的操作:首先在类中必须有对象,不能是null,例如,private Date birthday=new Date();。操作的是对象的属性而不是整个对象,例如,BeanUtils.setProperty(bean,"birthday.time",121);
2.3.1属性操作
设置:BeanUtils.setProperty(Object bean,String propertyName,String propertyValue);
获取:BeanUtils.getProperty(Object bean,String PropetyName);
(1) propertyName直接是属性名: BeanUtils.getProperty(myBean,"name");
(2) 直接访问内嵌对象的属性,只要使用点号分隔
BeanUtils.getProperty(myBean, "address.city");
(3)ArrayList和数组:属性名[索引值]
BeanUtils.setProperty(myBean, "product[1]", "NOTES SERVER");
BeanUtils.getIndexedProperty(myBean,"product",1);
BeanUtils.getProperty(myBean, "product[1]"));
(4)Map类型:属性名(key值)
BeanUtils.setProperty(myBean, "map",myMap);
BeanUtils.getProperty(myBean, "map(key)");
BeanUtils.getMappedProperty(myBean, "map","key");
static java.util.Map describe(java.lang.Object bean)
-->这个方法返回一个Object中所有的可读属性,并将属性名/属性值放入一个Map中
static void populate(java.lang.Object bean, java.util.Map properties)
-->map属性放到bean中
2.3.2 bean copy
static void copyProperties(java.lang.Object dest, java.lang.Object orig)
将一个对象的属性值复制到另一个对象的属性,需要保证属性一致。
BeanUtils.copyProperties VS PropertyUtils.copyProperties
相同点:两个bean存在name相同的属性进行处理,无论是源bean或者目标bean多出的属性均不处理。
区别:
(1)BeanUtils是相同name并且类型之间支持转换的属性可以处理
PropertyUtils不支持类型转换必须是类型和name一样才处理。
(2)对null的处理:PropertyUtils支持为null
BeanUtils对部分属性不支持null的情况,具体为下:
date类型不支持:异常 dateorg.apache.commons.beanutils.ConversionException: No value specified for 'Date'
Ineger、Boolean、Long等不支持: 转为0;
string:支持,值为null
(3)BeanUtils的高级功能org.apache.commons.beanutils.Converter接口可以自定义类型之间的转化。
PropertyUtils返回Object,由于不做类型转化,用PropertyUtils在速度上会有很大提高。
用BeanUtils无法返回一个对象(除非自己写一个Converter),它会自动进行类型转化,然后返回String。
对于想返回java类或自定义类的话,还是请使用PropertyUtils
· 关于类型转换的例子:
源bean有属性: private Long dateVal;
目标bean有属性:private Date dateVal;
使用 PropertyUtils,会保错:Caused by: java.lang.IllegalArgumentException: argument type mismatch
使用BeanUtils,则相当于new date(dateVal)
· 对于自定义的对象类型属性 都是浅copy :
比如都有属性:private Base base; Base有一个属性String Test;
new.getBase().setTest("new");
那么old.getBase().getTest()也为new
· 性能:get,set《PropertyUtils《BeanUtils
2.3.4 转换器
转换器用来将输入数据转换成需要的数据类型,同时提供统一的接口,方便客户代码使用和扩展。
Commons-beanutils包中,所有转换器都从org.apache.commons.beanutils.Converter接口集成,添加自己需要的实现。
Converter子类包含的都是转换器的实现,一般情况下,不需要直接实例化这些类,只需要使用ConvertUtil中convert方法,就可以进行数据类型的转换。高级用户不但可以使用默认的转换方式,还可以向ConvertUtils中注册新的或替代原有的转换器,实现需要的业务逻辑。
转换器接口的详细信息如下
Converter:BeanUtil框架中使用的类型转换接口,可以将输入数据转换成需要的类型
转换器分为以下三个部分:
2.3.4.1 数组转换器
这些类继承自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异常。
数组转换器的实现被封装在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,0被转换成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. 部分类的类型转换时存在缺陷,不能正常转换。
2.3.4.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,没有从源头杜绝这种情况发生,虽然这样设计更加灵活,但弊大于利,最好是在类创建时进行校验
2.3.4.3 地区敏感的转换器
地区敏感转换器都被封装在org.apache.commons.beanutils.locale和org.apache.commons.beanutils.locale.converters包中,前者提供一个集成自Converter的通用接口,后者提供这个接口的具体实现。
地区敏感转换器主要实现了当需要分地区进行转换时,需要进行的操作。主要功能是将带有不同地区特征的字符串转换成数字和时间日期类型对象。
地区敏感转换器中所有参数参数都是直接从构造函数中输入的,没有get和set,代码冗余度很大,需要重构。
类名 | 描述 |
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 | 将字符串转换成数字的字符串形式。 好像没有什么实际的作用 |
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。
把pattern格式的value转换成标准格式的相应的XXX类。这些类可以分为两大类:一类为时间,一类为数值。
时间类最后都会通过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);
数值类最后都会通过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异常。
2.3.4.4 转换器工具类
转换器相关的工具类是外界实际使用的接口,默认情况下,系统会向工具类中注册上述各种类型数据的转换器对象,用户可以自定义这些注册信息,添加,修改或删除自己不需要的转换器,还可以将自己实现的类型注册到转换器中。
转换器工具类的详细信息如下:
类名 | 描述 |
ConvertUtils | 将字符串对象转换成相应类型的对象。如,将String对象转换成Integer类型的对象,或将String对象转换成Integer数组对象。 默认使用系统自定义的转换器,但接口开放,可以自定义转换器进行数据类型转换。 |
ConvertUtilsBean | 实际进行数据转换的类。 |
LocaleConvertUtils | 和ConvertUtils作用类似,在转换的过程中根据不同的地区进行不用的转换,适用于地区敏感的数据。 |
LocaleConvertUtilsBean |
转换器这一套代码中实现了字符串向Java中各种数据类型的转换,这样在转换数据类型时不需要了解各种数据类型的转换API,只需要知道Converter接口即可,方便了开发人员编写代码。但这套代码也有很多不足,其中最大的就是代码冗余的问题,小到函数内部的实现,大到整个的类结构,或多或少的都存在代码复制粘贴的影子,可以通过重构让代码变得更加清晰
2.3.4.5 Utils类
所有的XXXUtils类都提供的是静态方法,可以直接调用,其主要实现都在相应的XXXUtilsBean中:
(1)BeanUtils ——> BeanUtilsBean
(2)ConvertUtils ——> ConvertUtilsBean
ConvertUtils 是ConvertUtilsBean类的一个简单封装,即ConvertUtils中的所有方法都是通过直接调用ConvertUtilsBean中的同名方法实现的。如果你需要更复杂的功能,就使用ConvertUtilsBean,否则使用ConvertUtils。
ConvertUtilsBean 通过一个HashMap管理所有的XXXConverter。这个HashMap的key为XXX的类全名,值为相应的XXXConverter对象。通过deregister()方法,初始化这个HashMap。这个初始化方法会为每一个XXXConverter类提供一个缺省的值。用户可以动过 setDefaultXXX(...)方法来自行设置XXXConverter对象的缺省值。这个类还提供了convert(...)方法,对 String value进行相应的转化。
(3)PropertyUtils ——> PropertyUtilsBean
PropertyUtils 是PropertyUtilsBean类的一个简单封装,同样它的所有方法都是通过直接调用PropertyUtilsBean 中同名方法实现的。
PropertyUtilsBean 对DynaBean或者一个java标准Bean中的属性动态的赋值和取值(非通过getXXX和setXXX方法)。
这个类支持多层嵌套,比如:XXX[i].YYY(key).ZZZ,那么它会为你得到或者设置ZZZ的属性。
//对XXX(key)格式的name设值 :setMappedProperty(Object bean, String name,String key, Object value)
//对XXX[i]格式的name设值 : setIndexed
常用的工具类有以下几个部分:
- PropertyUtils:对JavaBean中的属性值进行操作。
- MethodUtils:使用反射的方式请求bean中的方法。
- ConstructorUtils:使用反射的方式构造Bean的新实例。
- BeanUtils:对JavaBean提供拷贝,赋值等操作。
- LocaleBeanUtils:和BeanUtils功能类似,但还可以提供地区敏感数据的操作。
- 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 |
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中的简单属性赋值 |
2.3.6 动态bean
我们知道,每一个JavaBean对象中包含一个Class对象,这个对象是单实例的并且在当前类加载器中全局唯一,由JVM维护,用来存储Bean中的属性描述信息,这些信息在运行时无法修改。
动态bean符合JavaBean架构的基本思想,每一个DynaBean实例有一个DynaClass对象,这个对象在同类DynaBean中唯一,但可以动态的对属性进行增删改的操作。这样就弥补了原来JavaBean架构中当Bean定义后不能对Bean中属性进行扩展的缺点,同时,提供了对bean中属性进行get/set的统一工具类,这些工具类的接口可以兼容动态Bean、标准Bean,以及映射(map)。DynaBean并不是Java中所定义的Bean,而是一种“假”的Bean。因为它并不是通过getXXX和setXXX方法,对XXX属性进行取值和设值的。它通过一个实现了DynaClass接口的类,帮助管理其所有的属性的类别,而自己则管理对XXX属性值的设定和获取。在设值的时候会通过与 name对应的DynaProperty对象,检查赋值的类别是否正确。
1.属性
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。
类名 | 描述 |
DynaProperty | 动态bean中的属性,由属性名,属性类型两部分组成,对于数组、链表这类复杂类型,还加入了内容类型的概念,用来描述这些复杂数据接口内部对象的类型。 |
2.class对象
动态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对象 |
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));