一、概述
第一次看到BeanUtils包,是在Struts项目中,作为Struts一个工具来使用的,
估计功能越弄越强,就移到Common项目中了吧。
BeanUtils一共有四个package:
org.apache.commons.beanutils
org.apache.commons.beanutils.converters
org.apache.commons.beanutils.locale
org.apache.commons.beanutils.locale.converters
后三个包主要是用于数据的转换,围绕着一个Converter接口,该接口只有一个方法:
java.lang.Object convert(java.lang.Class type, java.lang.Object value) ,
用于将一个value转换成另一个类型为type的Object。在一些自动化的应用中应该会有用。
这里不作评论,以后有兴趣了,或者觉得有用了,再行研究。
这里只讲第一个包。
二、测试用的Bean
在开始所有的测试之前,我写了一个简单的Bean,以便于测试,代码如下:
java 代码
- package test.jakarta.commons.beanutils;
-
-
-
-
-
- public class Month {
- private int value;
- private String name;
- private int[] days={11,22,33,44,55};
-
- public Month(int v, String n){
- value=v;
- name=n;
- }
-
-
-
-
-
- public String getName() {
- return name;
- }
-
-
-
-
-
- public int getValue() {
- return value;
- }
-
-
-
-
-
- public void setName(String name) {
- this.name = name;
- }
-
-
-
-
-
- public void setValue(int value) {
- this.value = value;
- }
-
-
-
-
- public String toString() {
- return value+"("+name+")";
- }
-
- public int[] getDays() {
- return days;
- }
-
- public void setDays(int[] is) {
- days = is;
- }
-
- }
三、BeanUtils
这是一个主要应用于Bean的Util(呵呵,这个解释很绝吧),以下是其中几个方法的例子
//static java.util.Map describe(java.lang.Object bean)
//这个方法返回一个Object中所有的可读属性,并将属性名/属性值放入一个Map中,另外还有
//一个名为class的属性,属性值是Object的类名,事实上class是java.lang.Object的一个属性
java 代码
- Month month=new Month(1, "Jan");
-
- try {
- Map map=BeanUtils.describe(month);
- Set keySet=map.keySet();
- for (Iterator iter = keySet.iterator(); iter.hasNext();) {
- Object element = (Object) iter.next();
- System.out.println("KeyClass:"+element.getClass().getName());
- System.out.println("ValueClass:"+map.get(element).getClass().getName());
- System.out.print(element+"\t");
- System.out.print(map.get(element));
- System.out.println();
- }
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- }
- 输出为:
- KeyClass:java.lang.String
- ValueClass:java.lang.String
- value 1
- KeyClass:java.lang.String
- ValueClass:java.lang.String
- class class test.jakarta.commons.beanutils.Month
- KeyClass:java.lang.String
- ValueClass:java.lang.String
- name Jan
注意到所有Map中的key/value都是String,而不管object中实际的值是多少。
与此对应的还有static void populate(java.lang.Object bean, java.util.Map properties)
用于将刚才describe出的Map再装配成一个对象。
再看这样一段代码
曹晓钢也许还记得,为了取一个不确定对象的property,着实花了不少时间,
难度不大,但要做到100%的正确,仍然需要付出很大的精力。
java 代码
-
- Month month=new Month(1, "Jan");
-
- try {
- System.out.println(BeanUtils.getProperty(month,"value"));
- } catch (Exception e) {
- e.printStackTrace();
- }
-
-
- 与getProperty类似的还有getIndexedProperty, getMappedProperty,
- 以getIndexedProperty为例:
- Month month=new Month(1, "Jan");
-
- try {
- System.out.println(BeanUtils.getIndexedProperty(month,"days",1));
- System.out.println(BeanUtils.getIndexedProperty(month,"days[1]"));
- } catch (Exception e) {
- e.printStackTrace();
- }
- 这两个调用是相同的。
BeanUtils中还有一个方法:
static void copyProperties(java.lang.Object dest, java.lang.Object orig)
它真是太有用,还记得struts中满天飞的都是copyProperties,我甚至怀疑整个BeanUtils最初
是不是因为这个方法的需求才写出来的。
它将对象orig中的属性复制到dest中去。
四、PropertyUtils
这个类和BeanUtils类很多的方法在参数上都是相同的,但返回值不同。
BeanUtils着重于"Bean",返回值通常是String,而PropertyUtils着重于属性,
它的返回值通常是Object。
五、ConstructorUtils
这个类中的方法主要分成两种,一种是得到构造方法,一种是创建对象。
事实上多数时候得到构造方法的目的就是创建对象,这里只介绍一下创建对象。
//static java.lang.Object ConstructorUtils.invokeConstructor
//(java.lang.Class klass, java.lang.Object[] args)
//根据一个java.lang.Class以及相应的构造方法的参数,创建一个对象。
Object obj=ConstructorUtils.invokeConstructor(Month.class, {new Integer(1), "Jan"});
Month month=(Month)obj;
try {
System.out.println(BeanUtils.getProperty(month,"value"));
} catch (Exception e) {
e.printStackTrace();
}
输出证明,构造方法的调用是成功的。
如果需要强制指定构造方法的参数类型,可以这样调用:
Object[] args={new Integer(1), "Jan"};
Class[] argsType={int.class, String.class};
Object obj;
obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
Month month=(Month)obj;
System.out.println(BeanUtils.getProperty(month,"value"));
argsType指定了参数的类型。
六、ConstructorUtils补遗
创建对象还有一个方法:invokeExactConstructor,该方法对参数要求
更加严格,传递进去的参数必须严格符合构造方法的参数列表。
例如:
Object[] args={new Integer(1), "Jan"};
Class[] argsType={int.class, String.class};
Object obj;
//下面这句调用将不会成功,因为args[0]的类型为Integer,而不是int
//obj = ConstructorUtils.invokeExactConstructor(Month.class, args);
//这一句就可以,因为argsType指定了类型。
obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
Month month=(Month)obj;
System.out.println(BeanUtils.getProperty(month,"value"));
七、MethodUtils
与ConstructorUtils类似,不过调用的时候,通常需要再指定一个method name的参数。
八、DynaClass/DynaBean
这似乎是BeanUtils中最有趣的部分之一了,很简单,简单到光看这两个接口中的方法会不明白
为什么要设计这两个接口。不过看到ResultSetDynaClass后,就明白了。下面是java doc中的代码:
ResultSet rs = ...;
ResultSetDynaClass rsdc = new ResultSetDynaClass(rs);
Iterator rows = rsdc.iterator();
while (rows.hasNext()) {
DynaBean row = (DynaBean) rows.next();
... process this row ...
}
rs.close();
原来这是一个ResultSet的包装器,ResultSetDynaClass实现了DynaClass,它的iterator方法返回一个
ResultSetIterator,则是实现了DynaBean接口。
在获得一个DynaBean之后,我们就可以用
DynaBean row = (DynaBean) rows.next();
System.out.println(row.get("field1")); //field1是其中一个字段的名字
再看另一个类RowSetDynaClass的用法,代码如下:
java 代码
- String driver="com.mysql.jdbc.Driver";
- String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK";
- String username="root";
- String password="";
-
- java.sql.Connection con=null;
- PreparedStatement ps=null;
- ResultSet rs=null;
- try {
- Class.forName(driver).newInstance();
- con = DriverManager.getConnection(url);
- ps=con.prepareStatement("select * from forumlist");
- rs=ps.executeQuery();
-
- while(rs.next()){
- System.out.println(rs.getString("name"));
- }
- rs.beforeFirst();
-
- RowSetDynaClass rsdc = new RowSetDynaClass(rs);
- rs.close();
- ps.close();
- List rows = rsdc.getRows();
- for (int i = 0; i <rows.size(); i++) {
- DynaBean b=(DynaBean)rows.get(i);
- System.out.println(b.get("name"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- finally{
- try {
- con.close();
- } catch (Exception e) {
- }
- }
是不是很有趣?封装了ResultSet的数据,代价是占用内存。如果一个表有10万条记录,rsdc.getRows()
就会返回10万个记录。@_@
需要注意的是ResultSetDynaClass和RowSetDynaClass的不同之处:
1,ResultSetDynaClass是基于Iterator的,一次只返回一条记录,而RowSetDynaClass是基于
List的,一次性返回全部记录。直接影响是在数据比较多时ResultSetDynaClass会比较的快速,
而RowSetDynaClass需要将ResultSet中的全部数据都读出来(并存储在其内部),会占用过多的
内存,并且速度也会比较慢。
2,ResultSetDynaClass一次只处理一条记录,在处理完成之前,ResultSet不可以关闭。
3,ResultSetIterator的next()方法返回的DynaBean其实是指向其内部的一个固定
对象,在每次next()之后,内部的值都会被改变。这样做的目的是节约内存,如果你需要保存每
次生成的DynaBean,就需要创建另一个DynaBean,并将数据复制过去,下面也是java doc中的代码:
java 代码
- ArrayList results = new ArrayList();
- ResultSetDynaClass rsdc = ...;
- DynaProperty properties[] = rsdc.getDynaProperties();
- BasicDynaClass bdc =
- new BasicDynaClass("foo", BasicDynaBean.class,
- rsdc.getDynaProperties());
- Iterator rows = rsdc.iterator();
- while (rows.hasNext()) {
- DynaBean oldRow = (DynaBean) rows.next();
- DynaBean newRow = bdc.newInstance();
- PropertyUtils.copyProperties(newRow, oldRow);
- results.add(newRow);
- }
事实上DynaClass/DynaBean可以用于很多地方,存储各种类型的数据。自己想吧。嘿嘿。
九、自定义的CustomRowSetDynaClass
两年前写过一个与RowSetDynaClass目标相同的类,不过多一个功能,就是分页,只取需要的数据,
这样内存占用就会减少。
先看一段代码:
java 代码
- String driver="com.mysql.jdbc.Driver";
- String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK";
- String username="root";
- String password="";
-
- java.sql.Connection con=null;
- PreparedStatement ps=null;
- ResultSet rs=null;
- try {
- Class.forName(driver).newInstance();
- con = DriverManager.getConnection(url);
- ps=con.prepareStatement("select * from forumlist order by name");
- rs=ps.executeQuery();
-
-
-
-
-
-
-
-
- CustomRowSetDynaClass rsdc = new CustomRowSetDynaClass(rs, 2, 5);
-
- rs.close();
- ps.close();
- List rows = rsdc.getRows();
- for (int i = 0; i <rows.size(); i++) {
- DynaBean b=(DynaBean)rows.get(i);
- System.out.println(b.get("name"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- finally{
- try {
- con.close();
- } catch (Exception e) {
- }
- }
在这里用到了一个CustomRowSetDynaClass类,构造方法中增加了page和pageSize两个参数,
这样,不管数据库里有多少条记录,最多只取pageSize条记录,若pageSize==-1,则功能和
RowSetDynaClass一样。这在大多数情况下是适用的。该类的代码如下:
java 代码
- package test.jakarta.commons.beanutils;
-
- import java.io.*;
- import java.sql.*;
- import java.util.*;
-
- import org.apache.commons.beanutils.*;
-
-
-
-
-
-
-
-
-
- public class CustomRowSetDynaClass implements DynaClass, Serializable {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public CustomRowSetDynaClass(ResultSet resultSet) throws SQLException {
-
- this(resultSet, true);
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public CustomRowSetDynaClass(ResultSet resultSet, boolean lowerCase)
- throws SQLException {
-
- this(resultSet, 1, -1, lowerCase);
-
- }
-
- public CustomRowSetDynaClass(
- ResultSet resultSet,
- int page,
- int pageSize,
- boolean lowerCase)
- throws SQLException {
-
- if (resultSet == null) {
- throw new NullPointerException();
- }
- this.lowerCase = lowerCase;
- this.page = page;
- this.pageSize = pageSize;
-
- introspect(resultSet);
- copy(resultSet);
-
- }
-
- public CustomRowSetDynaClass(ResultSet resultSet, int page, int pageSize)
- throws SQLException {
- this(resultSet, page, pageSize, true);
- }
-
-
-
-
-
-
-
- protected boolean lowerCase = true;
-
- protected int page = 1;
- protected int pageSize = -1;
-
-
-
-
-
- protected DynaProperty properties[] = null;
-
-
-
-
-
-
-
- protected Map propertiesMap = new HashMap();
-
-
-
-
-
-
- protected List rows = new ArrayList();
-
-
-
-
-
-
-
-
-
- public String getName() {
-
- return (this.getClass().getName());
-
- }
-
-
-
-
-
-
-
-
-
-
- public DynaProperty getDynaProperty(String name) {
-
- if (name == null) {
- throw new IllegalArgumentException("No property name specified");
- }
- return ((DynaProperty) propertiesMap.get(name));
-
- }
-
-
-
-
-
-
- public DynaProperty[] getDynaProperties() {
-
- return (properties);
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
- public DynaBean newInstance()
- throws IllegalAccessException, InstantiationException {
-
- throw new UnsupportedOperationException("newInstance() not supported");
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public List getRows() {
-
- return (this.rows);
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- protected void copy(ResultSet resultSet) throws SQLException {
- int abs = 0;
- int rowsCount = 0;
- int currentPageRows = 0;
- resultSet.last();
- rowsCount = resultSet.getRow();
- if (pageSize != -1) {
- int totalPages = (int) Math.ceil(((double) rowsCount) / pageSize);
- if (page > totalPages)
- page = totalPages;
- if (page < 1)
- page = 1;
- abs = (page - 1) * pageSize;
-
-
- } else
- pageSize = rowsCount;
- if (abs == 0)
- resultSet.beforeFirst();
- else
- resultSet.absolute(abs);
-
- while (resultSet.next() && ++currentPageRows <= pageSize) {
- DynaBean bean = new BasicDynaBean(this);
- for (int i = 0; i < properties.length; i++) {
- String name = properties[i].getName();
- bean.set(name, resultSet.getObject(name));
- }
- rows.add(bean);
- }
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
- protected void introspect(ResultSet resultSet) throws SQLException {
-
-
- ArrayList list = new ArrayList();
- ResultSetMetaData metadata = resultSet.getMetaData();
- int n = metadata.getColumnCount();
- for (int i = 1; i <= n; i++) {
- DynaProperty dynaProperty = createDynaProperty(metadata, i);
- if (dynaProperty != null) {
- list.add(dynaProperty);
- }
- }
-