Apache Commons Beanutils 二 (动态Bean - DynaBeans)

相关背景

上一篇介绍了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

时间: 2024-10-25 13:15:21

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

Apache Commons Beanutils教程一(访问Bean属性)

BeanUtils简要描述 beanutils,顾名思义,是java bean的一个工具类,可以帮助我们方便的读取(get)和设置(set)bean属性值.动态定义和访问bean属性: 细心的话,会发现其实JDK已经提供了一个java.beans包,同样可以实现以上功能,只不过使用起来比较麻烦,所以诞生了apache commons beanutils: 看源码就知道,其实apache commons beanutils就是基于jdk的java.beans包实现的. Java Bean 在介绍a

Apache commons beanutils简介和使用

在一般的写bean组件的时候,都必须要写setter和getter方法,当然假如我们事先已经知道bean的相关属性和方法,写bean是比较简单的,但是组件太多的时候,重复编写经常是枯燥乏味令人厌烦的.但当有些时候我么需要调用动态对象的属性的时候,我们应该怎么来设定和获取对象的属性呢?BeanUtils就可以帮助我们来简化解决这个问题. 目前最新的版本是1.9. 在日常的开发中,下面类似的代码应该是非常的眼熟. DynaActionForm daf =(DynaActionForm)form ;

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

前言 前面已经学习了Apache Commons Beanutils包里的PropertyUtils和动态bean,接下来将学习剩下的几个工具类,个人觉得还是非常实用的,特别是CollectionUtils: BeanUtils 简单介绍下两个方法的使用,populate和copyProperties, populate可以帮助我们把Map里的键值对值拷贝到bean的属性值中: copyProperties,顾名思义,帮我们拷贝一个bean的属性到另外一个bean中,注意是浅拷贝 如下示例: /

myeclipse的项目导入到eclipse下,com.sun.org.apache.commons.beanutils.BeanUtils不能导入

com.sun.org.apache.commons.beanutils.BeanUtils这个包不能引入了怎么办自己下了个org.apache.commons的jar包了之后,改成import org.apache.commons.beanutils.BeanUtils 编译能通过了.但是用到beanutils的时候会报错,java.lang.reflect.InvocationTargetException java.lang.NoClassDefFoundError: org/apache

Cannot find class [org.apache.commons.dbcp.BasicDataSource] for bean with name &#39;dataSource&#39; defined in class path resource

Cannot find class [org.apache.commons.dbcp.BasicDataSource] for bean with name 'dataSource' defined in class path resource 错误原因:缺少两个jar包 获得jar包方法:从maven仓库下载 网址:https://mvnrepository.com/ 下载需要的jar包,复制到lib目录下右击项目添加到工程中. Cannot find class [org.apache.co

Apache Commons Digester 二(规则模块绑定-RulesModule、异步解析-asyncParse、xml变量Substitutor、带参构造方法)

前言 上一篇对Digester做了基本介绍,也已经了解了Digester的基本使用方法,接下来将继续学习其相关特性,本篇主要涉及以下几个内容: 规则模块绑定,通过定义一个RulesModule接口实现类来完成规则的预先绑定,运行时重复使用 异步解析xml 解析xml中的变量,如${sys.user} 使用带参数的构造方法创建对象,参数来自xml节点数据 规则模块预先绑定 - RulesModule接口 在此之前,我们使用Digester的基本流程都是每次在程序运行时绑定规则,然后解析: 事实上,

java.lang.NoClassDefFoundError: org/apache/commons/beanutils/BeanUtils

在servlet类中使用Beanutils方法封装前端注册界面信息,浏览器中报错,状态码500,报错信息: 解决办法: 在Tomcat和web下的lib包中都要导入commons-beanutils-1.8.3.jar这个jar包. ^_^ 原文地址:https://www.cnblogs.com/kongieg/p/10126746.html

Apache commons Proxy简介和动态代理、动态拦截器实现

Apache Commons Proxy 是Apache 的一个之项目,封装了 Java 对象代理的一些常用方法.又叫做 动态代理. 动态代理的作用非常大,在很多底层框架中都会用得到,比如struts,Spring等都用到了动态代理,它的原理很简单,就是将你要使用的类,重新生成一个子类或本类,这样框架就可以利用这个新生成的类做一些事情,比如在该类的方法前后加一些代码. 设想一下,不用修改任何已经编写好的代码,只要使用动态代理就可以灵活的加入一些东西,将来要是不喜欢了,不用也不会影响原来的代码.

一篇关于apache commons类库的详解

原文 http://blog.csdn.net/wiker_yong/article/details/23551209 1.1. 开篇 在Java的世界,有很多(成千上万)开源的框架,有成功的,也有不那么成功的,有声名显赫的,也有默默无闻的.在我看来,成功而默默无闻的那些框架值得我们格外的尊敬和关注,Jakarta Commons就是这样的一个框架.如果你至少参与了一个中型规模的Java项目,那么我想有超过一大半的机会你都接触和使用到了Jakarta Commons,不管你自己有没有察觉.就我所