相关背景

上一篇介绍了PropertyUtils的用法,PropertyUtils主要是在不修改bean结构的前提下,动态访问bean的属性;

但是有时候,我们会经常希望能够在不定义一个Java类的前提下,动态决定这个类中包含哪些属性,并动态访问它们的属性值,比较典型的使用场景是作为SQL查询的结果集的bean;

为了支持以上特性,Apache Commons Beanutils包为我们提供了DynaBean接口、DynaClass接口;

举个简单例子如下:

       DynaProperty[] props = new DynaProperty[]{
new DynaProperty("address", java.util.Map.class),
new DynaProperty("subordinate", mypackage.Employee[].class),
new DynaProperty("firstName", String.class),
new DynaProperty("lastName", String.class)
};
BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props); DynaBean employee = dynaClass.newInstance();
employee.set("address", new HashMap());
employee.set("subordinate", new Employee[]{...});
employee.set("firstName", "Fred");
employee.set("lastName", "Flintstone"); DynaBean employee = ...; // 具体的DynaBean实现类 String firstName = (String) employee.get("firstName");
Address homeAddress = (Address) employee.get("address", "home");
Object subordinate = employee.get("subordinate", 2);

由于DynaBean和DynaClass都是接口,它们可以有多种实现形式,应用于多种场景。

接下来,会介绍在Apache Commons Beanutils包下,DynaBean和DynaClass接口不同的实现类;

当然,我们也可以自定义实现类来满足我们特定的需求;

基础实现类:BasicDynaBean和BasicDynaClass

先了解下这两个重要的实现,这两个类为DynaBean和DynaClass接口的基础实现类;

首先,我们可以这样创建一个DynaClass实例,其中类的成员属性是用DynaProperty类来描述的:

        DynaProperty[] props = new DynaProperty[]
{
new DynaProperty("address", java.util.Map.class),
new DynaProperty("subordinate", Employee[].class),
new DynaProperty("firstName", String.class),
new DynaProperty("lastName", String.class)
};
BasicDynaClass dynaClass = new BasicDynaClass("employee", null, props);

注意这里的Class<?> dynaBeanClass参数为空,看下源码就发现,如果为null的话,默认会使用BasicDynaBean.class;

有了BasicDynaClass实例后,我们就可以开始创建DynaBean实例了,并且可以调用DynaBean接口中定义的方法,如get和set来读写属性值,如下所示:

        DynaBean employee = dynaClass.newInstance();
employee.set("address", new HashMap<String, Object>());
employee.set("subordinate", new Employee[0]);
employee.set("firstName", "Fred");
employee.set("lastName", "Flintstone");

      System.out.println(employee.get("firstName"));

实现类:ResultSetDynaClass,处理数据库查询结果集

ResultSetDynaClass主要用于包装java.sql.ResultSet,即SQL查询时候返回的结果集;

不使用DynaBean的话,通常我们是这样处理的:

            String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery(sql); while (rs.next())
{
Long id = rs.getLong("id");
String name = rs.getString("name");
String address = rs.getString("address");
boolean state = rs.getBoolean("state"); System.out.print("id: " + id);
System.out.print(", name: " + name);
System.out.print(", address: " + address);
System.out.println(", state: " + state);
}

使用ResultSetDynaClass的话,我们可以这样做:

            String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery(sql);
Iterator<DynaBean> rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext())
{
DynaBean row = rows.next();
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
}

完整示例:

/*
* File Name: ResultSetDyna.java
* Description:
* Author: PiChen
* Create Date: 2017年5月30日
*/
package apache.commons.beanutils.example.dynabeans; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator; import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.ResultSetDynaClass; /**
*
* @author PiChen
* @version 2017年5月30日
*/ public class ResultSetDyna
{
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/demo"; static final String USER = "root";
static final String PASS = "root"; public static void main(String[] args)
{
Connection conn = null;
PreparedStatement stmt = null;
try
{
Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(DB_URL, USER, PASS); String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(sql); // while (rs.next())
// {
// Long id = rs.getLong("id");
// String name = rs.getString("name");
// String address = rs.getString("address");
// boolean state = rs.getBoolean("state");
//
// System.out.print("id: " + id);
// System.out.print(", name: " + name);
// System.out.print(", address: " + address);
// System.out.println(", state: " + state);
// } Iterator<DynaBean> rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext())
{
DynaBean row = rows.next();
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
} rs.close();
stmt.close();
conn.close();
}
catch (SQLException se)
{
se.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (stmt != null)
stmt.close();
}
catch (SQLException se2)
{
}
try
{
if (conn != null)
conn.close();
}
catch (SQLException se)
{
se.printStackTrace();
}
}
} }

实现类:RowSetDynaClass,处理数据库查询结果集,连接关闭后仍可使用

ResultSetDynaClass作为SQL查询结果集中的一个动态bean非常实用,但是仍然有一个严重的缺陷,就是使用ResultSetDynaClass的前提是要保证ResultSet一直处于打开状态,这对于分层结构的Web项目来说是非常不便的,因为我们经常需要将数据从dao层传到service层传到view层,而ResultSet在DAO层使用后往往会关闭掉;

为解决这个问题,引入了RowSetDynaClass实现类,与ResultSetDynaClass不同的是,它会自己在内存中拷贝一份数据,这样就保证了即使ResultSet关闭后,数据也能一直被访问到;不过同样也有缺点就是需要消耗性能用于拷贝数据以及占用堆内存空间;

如下是一个示例:

           Class.forName("com.mysql.jdbc.Driver");

            conn = DriverManager.getConnection(DB_URL, USER, PASS);

            String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(sql); RowSetDynaClass rowSet = new RowSetDynaClass(rs); rs.close();
stmt.close();
conn.close(); List<DynaBean> rowlist = rowSet.getRows();
for (DynaBean row : rowlist)
{
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
}

完整示例:

/*
* File Name: ResultSetDyna.java
* Description:
* Author: PiChen
* Create Date: 2017年5月30日
*/
package apache.commons.beanutils.example.dynabeans; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List; import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.RowSetDynaClass; /**
*
* @author PiChen
* @version 2017年5月30日
*/ public class RowSetDyna
{
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost/demo"; static final String USER = "root";
static final String PASS = "root"; public static void main(String[] args)
{
Connection conn = null;
PreparedStatement stmt = null;
try
{
Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(DB_URL, USER, PASS); String sql = "SELECT id, name, address, state FROM user";
stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(sql); RowSetDynaClass rowSet = new RowSetDynaClass(rs); rs.close();
stmt.close();
conn.close(); List<DynaBean> rowlist = rowSet.getRows();
for (DynaBean row : rowlist)
{
System.out.print("id: " + row.get("id"));
System.out.print(", name: " + row.get("name"));
System.out.print(", address: " + row.get("address"));
System.out.println(", state: " + row.get("state"));
}
}
catch (SQLException se)
{
se.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if (stmt != null)
stmt.close();
}
catch (SQLException se2)
{
}
try
{
if (conn != null)
conn.close();
}
catch (SQLException se)
{
se.printStackTrace();
}
}
} }

实现类:WrapDynaBean和WrapDynaClass,包装普通bean

使用WrapDynaBean,我们可以将普通的javabean包装成DynaBean,并非常简便的使用DynaBean提供的API方法来访问bean成员属性

示例:

        Employee e = new Employee();
e.setFirstName("hello"); DynaBean wrapper = new WrapDynaBean(e);
String firstName = (String) wrapper.get("firstName");
System.out.println(firstName);

注意,以上代码中,会间接的创建了WrapDynaClass实例,我们不需要直接处理它;

实现类:Lazy DynaBeans,简单易用的DynaBean实现

Lazy DynaBeans,正如其名,可以让我们省去很多工作,更加人性化的去使用DynaBean,

Lazy DynaBeans有如下特性:

1、自动添加bean属性,当我们调用set(name, value)方法时,如果属性不存在,会自动添加该属性;

2、List、Array属性自动扩容,

3、List、Array属性里的内部元素可以自动创建,实例化

4、Map属性也可以自动创建,实例化

5、...

简单的说,使用Lazy DynaBeans的话,你可以大胆调用DynaBean的set、get方法,而不必担心没有属性不存在,集合数组空间不够等问题,Lazy DynaBeans会帮我们自动处理;

如下是一个LazyDynaBean例子:

        DynaBean dynaBean = new LazyDynaBean();

        dynaBean.set("foo", "bar");                   // simple

        dynaBean.set("customer", "title", "Mr");      // mapped
dynaBean.set("customer", "surname", "Smith"); // mapped dynaBean.set("users", 0, new User()); // indexed
dynaBean.set("users", 1, new User()); // indexed
dynaBean.set("users", 2, new User()); // indexed System.out.println(dynaBean.get("customer", "title"));

如下是一个LazyDynaMap例子:

       DynaBean dynaBean = new LazyDynaMap();

        dynaBean.set("foo", "bar");                   // simple

        dynaBean.set("customer", "title", "Mr");      // mapped
dynaBean.set("customer", "surname", "Smith"); // mapped dynaBean.set("users", 0, new User()); // indexed
dynaBean.set("users", 1, new User()); // indexed
dynaBean.set("users", 2, new User()); // indexed System.out.println(dynaBean.get("customer", "title")); //转成Map对象
Map<String, Object> myMap = ((LazyDynaBean) dynaBean).getMap();
System.out.println(myMap);

LazyDynaList例子,详见API文档

  LazyDynaList dynaBean = new LazyDynaList();
dynaBean.setElementType(User.class); User u = new User();
u.setName("hello"); dynaBean.add(1, u); System.out.println(dynaBean.size()); User[] users = (User[])dynaBean.toArray();//转化为数组
System.out.println(users[1].getName()); WrapDynaBean w = (WrapDynaBean) dynaBean.get(1);
System.out.println(w.get("name"));

LazyDynaClass示例:

Lazy DynaBeans可以让我们不受控制的添加任意类型的bean属性,但是有时候,我们还是希望能控制某个bean属性的数据类型,如下,是一个示例:

        MutableDynaClass dynaClass = new LazyDynaClass();    // create DynaClass

        dynaClass.add("amount", java.lang.Integer.class);    // add property
dynaClass.add("users", User[].class); // add indexed property
dynaClass.add("orders", TreeMap.class); // add mapped property DynaBean dynaBean = new LazyDynaBean(dynaClass);
dynaBean.set("amount_", "s");
dynaBean.set("amount", "s");//报错,需要为整型
dynaBean.set("users", 1);//报错,需要维数组 System.out.println(dynaBean.get("amount"));

参考资料

http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.3/apidocs/org/apache/commons/beanutils/package-summary.html

源码

https://github.com/peterchenhdu/apache-commons-beanutils-example

Apache Commons Beanutils 二 (动态Bean - DynaBeans)的更多相关文章

  1. Apache Commons Beanutils 一 (使用PropertyUtils访问Bean属性)

    BeanUtils简要描述 beanutils,顾名思义,是java bean的一个工具类,可以帮助我们方便的读取(get)和设置(set)bean属性值.动态定义和访问bean属性: 细心的话,会发 ...

  2. 对于Java Bean的类型转换问题()使用 org.apache.commons.beanutils.ConvertUtils)

    在进行与数据库的交互过程中,由数据库查询到的数据放在 map 中,由 map 到 JavaBean 的过程中可以使用 BeanUtils.populate(map,bean)来进行转换 这里要处理的问 ...

  3. Apache Commons BeanUtils

    http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.2/apidocs/org/apache/commons/beanut ...

  4. Apache Commons Beanutils 三 (BeanUtils、ConvertUtils、CollectionUtils...)

    前言 前面已经学习了Apache Commons Beanutils包里的PropertyUtils和动态bean,接下来将学习剩下的几个工具类,个人觉得还是非常实用的,特别是CollectionUt ...

  5. org.springframework.beans.BeanUtils与org.apache.commons.beanutils.BeanUtils的copyProperties用法区别

    知识点 org.springframework.beans.BeanUtils与org.apache.commons.beanutils.BeanUtils都提供了copyProperties方法,作 ...

  6. 再续前缘-apache.commons.beanutils的补充

    title: 再续前缘-apache.commons.beanutils的补充 toc: true date: 2016-05-32 02:29:32 categories: 实在技巧 tags: 插 ...

  7. 关闭log4j 输出 DEBUG org.apache.commons.beanutils.*

    2016-03-23 10:52:26,860 DEBUG org.apache.commons.beanutils.MethodUtils - Matching name=getEPort on c ...

  8. Apache Commons Beanutils对象属性批量复制(pseudo-singleton)

    Apache Commons Beanutils为开源软件,可在Apache官网http://commons.apache.org/proper/commons-beanutils/download_ ...

  9. org.apache.commons.beanutils.BeanMap简单使用例子

    一.org.apache.commons.beanutils.BeanMap; 将一个java bean允许通过map的api进行调用, 几个支持的操作接口: Object get(Object ke ...

随机推荐

  1. 114. Flatten Binary Tree to Linked List 把二叉树变成链表

    [抄题]: Given a binary tree, flatten it to a linked list in-place. For example, given the following tr ...

  2. 50-2018 蓝桥杯省赛 B 组模拟赛(五)

    1.结果填空:矩阵求和 import java.math.BigInteger; import java.util.HashSet; public class Main{ public static ...

  3. <jsp:forward page='/index' />

  4. 大型互联网 b2b b2c o2o 电子商务微服务云平台

    鸿鹄云商大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B.B2C.C2C.O2O.新零售.直播电商等子平台. 分布式.微服务.云架构电子商务平台 java ...

  5. linux学习 (Linux就该这么学)

    明天周五了,7点准时上课,加油努力学习,12月份要考试了,心里没有底,加油吧!感觉要学的真多,! redhat7,,安装图形界面  yum install -y

  6. small_trick_on_IT/PC

    1.浏览器下ctrl+F可实现文本查找 其余还有 2.将软件目录放到环境变量Path下,Ctrl+R输入.exe软件名即可调用. (tips:可把常用软件建立快捷方式,统一放在某一目录下噢!)

  7. iPhone屏幕分辨率和适配规则 图片文字适配

    基本概念 - 逻辑分辨率 pt (point),物理分辨率 px (pixel) - 缩放因子 scale 或者 dpr, scale ≈ px / pt - 缩放采样 例如 iPhone 6 Plu ...

  8. The current state of generics in Delphi( 转载)

    The current state of generics in Delphi   To avoid duplication of generated code, the compiler build ...

  9. TJOI2018 简要题解

    数学计算 用线段树记录之前乘过的每一个数,作除法时把原本的乘数改成111即可. 代码: #include<bits/stdc++.h> #define lc (p<<1) #d ...

  10. fortran77读写文本文档

    PROGRAM WRITETEXT IMPLICIT NONE INTEGER,PARAMETER :: NE=!fortran90 语法定义变量 DOUBLE PRECISION A(,),B(,) ...