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"));
            }
复制代码

完整示例:

  View Code

实现类: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"));
            }
复制代码

完整示例:

  View Code

实现类: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

 


本文转自风一样的码农博客园博客,原文链接:http://www.cnblogs.com/chenpi/p/6919343.html,如需转载请自行联系原作者

相关文章
|
Apache
java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream
java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream
619 0
|
5月前
|
消息中间件 Kubernetes Kafka
AutoMQ 产品动态 | 发布 1.1.0,兼容至 Apache Kafka 3.7,支持 Kaf
AutoMQ 产品动态 | 发布 1.1.0,兼容至 Apache Kafka 3.7,支持 Kaf
73 0
AutoMQ 产品动态 | 发布 1.1.0,兼容至 Apache Kafka 3.7,支持 Kaf
|
6月前
|
Java 数据库连接 Apache
深入理解Apache Commons Pool2池化技术
深入理解Apache Commons Pool2池化技术
|
7月前
|
Java Apache Spring
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求
【5月更文挑战第4天】Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求;Cglib BeanCopier用于转换为Cglib代理对象;Apache PropertyUtils处理属性操作;Dozer支持复杂对象映射。选择工具取决于具体需求,如需精细控制或对象映射,推荐Dozer或Apache PropertyUtils。Apache BeanUtils可能因潜在的封装性破坏被禁用。
73 3
|
7月前
|
算法 Java Apache
Apache Commons
Apache Commons是一个开源项目,提供了一系列的工具和库,用于简化Java开发中的常见任务。
69 1
|
消息中间件 Java Kafka
Apache Kafka - 灵活控制Kafka消费_动态开启/关闭监听实现
Apache Kafka - 灵活控制Kafka消费_动态开启/关闭监听实现
1025 1
|
Java Apache Spring
Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer 那么,我们到底应该选择哪种工具类更加合适呢?为什么Java开发手册中提到禁止使用Apache BeanUtils呢
Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer 那么,我们到底应该选择哪种工具类更加合适呢?为什么Java开发手册中提到禁止使用Apache BeanUtils呢
106 0
|
Dubbo 应用服务中间件 Apache
带你读《Apache Dubbo微服务开发从入门到精通》—— 五、 动态配置规则
带你读《Apache Dubbo微服务开发从入门到精通》—— 五、 动态配置规则
130 17
|
移动开发 前端开发 Java
Spring MVC-09循序渐进之文件上传(基于Apache Commons FileUpload)
Spring MVC-09循序渐进之文件上传(基于Apache Commons FileUpload)
106 0
|
Java 数据库连接 mybatis
项目依赖问题导致No qualifying bean of type 'org.apache.ibatis.session.SqlSessionFactory' available: more tha...
项目依赖问题导致No qualifying bean of type 'org.apache.ibatis.session.SqlSessionFactory' available: more tha...
372 0

推荐镜像

更多