拾贝壳

走过的路
随笔 - 39, 文章 - 1, 评论 - 14, 引用 - 0
数据加载中……

Commons BeanUtils

BeanUtils简介

这个组件的全称是Bean Introspection Utilites。是属于Jakarta Commons项目组的。主要是帮助构建javabean的属性操作的(getter,setter),已经提供一种动态定义和访问bean的属性。
接口摘要
Converter  通用数据类型转换器,可以被注册、使用用来管理对象类型的转换
DynaBean  一个java对象,这个对象的属性的名称,数据类型和值都能被动态改变
DynaClass  java.lang.class的一个模拟,用来给其它类实现DynaBean接口(类似java.lang.Object和java.lang.Class的关系)
MutableDynaClass  一个DynaClass的专门的扩展,允许属性动态的增加和减少。

类摘要
BasicDynaBean  
    DynaBean接口的最小实现
BasicDynaClass 
     DynaClass接口的最小实现
BeanUtils 
    工具方法,通过反射组装(populating)javabens的属性。
BeanUtilsBean 
    JavaBean属性的总体方法
ConstructorUtils 
   提供反射工具方法,用于构造函数, MethodUtils与此相仿
ContextClassLoaderLocal 
  
 A value that is provided per (thread) context classloader. 一个值,被提供了per (thread) context classloader.
ConvertingWrapDynaBean 
   DynaBean的一个实现,包装了标准的JavaBean实例,因此DynaBean APIs可以访问他的属性,尽管这些实现允许在属性设值的时候发生类型转换
ConvertUtils 
工具方法类,用于转换String类型变量的值到特定的类型变量的值,String类型的数组到特定类型的数组。
ConvertUtilsBean  
同上
DynaProperty 
METADATA,描述了一个DynaBean的一个属性
JDBCDynaClass 
提供常用的逻辑,用于JDBC实现 DynaClass
LazyDynaBean 
一种DynaBean,能够自动添加属性到DynaClass,而且提供 Lazy List和Lazy Map的性质
LazyDynaClass 
一种DynaClass,实现了MutableDynaClass接口
LazyDynaMap 
Provides a light weight DynaBean facade to a Map with lazy map/list processing.
为Map提供了轻量级的DynaBean facade
 MappedPropertyDescriptor  
一个MappedPropertyDescriptor描述了一个map化的属性
MethodUtils  
工具映射方法类,应用于一般方法而不是特定属性。
 MethodUtils.MethodDescriptor  Represents the key to looking up a Method by reflection. 代表了通过反射查找方法的KEY
 
PropertyUtils  
工具方法,用于利用Java Reflection APIs来帮助一般属性getter and setter的操作在Java对象上。
 PropertyUtilsBean   同上
 
ResultSetDynaClass  
DynaClass的实现,用于 DynaBeans,DynaBeans包装了java.sql.ResultSet的java.sql.Row对象
 
ResultSetIterator  
java.util.Iterator的实现,用ResultSetDynaClass .的iterator()方法得到。
 
RowSetDynaClass  
DynaClass 的实现,创建了一个 包含DynaBean的in-memory容器,代表了SQL查询的结果。
 
WrapDynaBean  
DynaBean的实现,他包装了一个标准的JavaBean实例,因此DynaBean APIs可以被用来访问它的属性
 
WrapDynaClass  
DynaClass的实现,用于包装了标准的 JavaBean实例的DynaBeans
 

异常摘要

BeanAccessLanguageException
    用于指出Bean Access Language 不能再给出的bean上执行查询

ConversionException
    
用于指出对Converter.convert()的调用没有成功

NestedNullException
  
用于指出Bean Access Language 不能再给出的bean上执行查询的原因是因为嵌套的bean引用为空

 

概要: 

背景:

JavaBeans 符合java api命名规范,它是它是java语言的组成体系之一。按照JavaBeans 设计模式可以带来许多便利。

JavaBeans Specification描述了完整的一套特点用来把任意一个类变成JavaBeans ,你最好读读这个文档,一些最基本的在此列出:

  •    类必须声明为public,提供一个不带参数的public构造函数。这样可以让其他工具或者应用程序动态的创建这个类的实例,而不需要事先知道被使用的类的名字。比如:
    String className = ...;
            Class beanClass = Class.forName(className);
            Object beanInstance = beanClass.newInstance();
  • 作为不带参数构造函数的一个结果,类的初始化和bean的行为的配置必须完全分离。典型的做法是定义一套properties,用来修改它的行为和和这个bean代表的数据。属性取名习惯做法是,以小写字母开头,由java合法字符组成。
  • 典型的,每一个property都有一个public的getter和setter方法来分别设置和取回属性的值 ,JavaBeans Specification 定义了这个规范。用get或者set作为前缀,并把属性的第一个字母大写紧跟其后。比如:
    public class Employee {
                public Employee();   // Zero-arguments constructor
                public String getFirstName();
                public void setFirstName(String firstName);
                public String getLastName();
                public void setLastName(String lastName);
                public Date getHireDate();
                public void setHireDate(Date hireDate);
                public boolean isManager();
                public void setManager(boolean manager);
                public String getFullName();
            }
  • 上面的例子,有个boolean值。boolean类型的属性是以is为前缀的,这样更容易理解。
  • 如果你既有getter又有setter,注意getter的返回值类型和setter的形参类型。另外,对同一个名字定义一个以上的类型不同的setter方法是和java规范不符的。
  • 并不是每个属性都需要get和set方法。由上面的例子我们就可以印证这一点。
  • 创建一些get和set方法不符合上面的规范也是有可能的。具体的您可以参照完整的java规范。
  • JavaBeans Specification 还定义了其他的一些规范。

用标准的java编码技术,在你事先知道调用的类,事先知道要关注的属性的情况下处理javabeans 是十分方便的。

外部支持:

commons-beanutils 需要一下一些包来运行:

Standard JavaBeans

背景:

如上所述,标准的写法可以给你的使用带来方便。但是当你事先并不知道哪个类会被调用或者哪个属性需要修改的时候怎么办呢?java提供了一些类(比如java.beans.Introspector,他能够在运行时检查类而且标志出属性的getter和setter方法),加上Reflection 机制来动态调用方法。但是,这些方法很难使用,而且暴露了过多的程序使用者不需要了解的基本结构的细节,BeanUtils的APIs企图简化动态setter和getter的方法。

PropertyUtils很好的满足我们的需求,这一章我们将深入介绍。

首先,这里来介绍一些深入的定义:

javabean提供的属性可以分成三类,一些被标准的JavaBeans规范所支持,而一些只被BeanUtils所支持

  • 简单类型(simple),最基本的属性类型包括java最原始的数据类型(整形,字符串形),或者稍微复杂一点的对象。
  • 索引类型(index),一个索引类型的属性存储了一个有序的对象(相同类型)容器,可以通过整型,非负索引值单独访问。或者,你可以干脆用一个数组来整体访问这个属性。作为JavaBeans specification扩展,BeanUtils包认为java.util.list类型的属性也可以这样来访问。
  • mapped,作为JavaBeans specification扩展,BeanUtils包认为java.util.map类型的属性是map化,你可以设置和取回这个属性通过String-valued键(key).

PropertyUtils 类中提共了get和set以上各种属性类型的多种多样的方法。在下面的代码片断中,假设这个类有两个实例。

 public class Employee {
        public Address getAddress(String type);
        public void setAddress(String type, Address address);
        public Employee getSubordinate(int index);
        public void setSubordinate(int index, Employee subordinate);
        public String getFirstName();
        public void setFirstName(String firstName);
        public String getLastName();
        public void setLastName(String lastName);
    }
Basic Property Access(基本属性访问方式)

利用如下apis:

  • PropertyUtils.getSimpleProperty(Object bean, String name)
  • PropertyUtils.setSimpleProperty(Object bean, String name, Object value)

利用这些方法,你可以动态操作这个对象,

Employee employee = ...;
    String firstName = (String)
      PropertyUtils.getSimpleProperty(employee, "firstName");
    String lastName = (String)
      PropertyUtils.getSimpleProperty(employee, "lastName");
    ... manipulate the values ...
    PropertyUtils.setSimpleProperty(employee, "firstName", firstName);
    PropertyUtils.setSimpleProperty(employee, "lastName", lastName);
For indexed properties, you have two choices,你有两种选择,可以参照下面的例子,要用到的APIS如下:

  • PropertyUtils.getIndexedProperty(Object bean, String name)
  • PropertyUtils.getIndexedProperty(Object bean, String name, int index)
  • PropertyUtils.setIndexedProperty(Object bean, String name, Object value)
  • PropertyUtils.setIndexedProperty(Object bean, String name, int index, Object value)

例子如下:

  Employee employee = ...;
    int index = ...;
    String name = "subordinate[" + index + "]";
    Employee subordinate = (Employee)
      PropertyUtils.getIndexedProperty(employee, name);

    Employee employee = ...;
    int index = ...;
    Employee subordinate = (Employee)
      PropertyUtils.getIndexedProperty(employee, "subordinate", index);
对于mapped属性,也有两种方式。先参照apis

  • PropertyUtils.getMappedProperty(Object bean, String name)
  • PropertyUtils.getMappedProperty(Object bean, String name, String key)
  • PropertyUtils.setMappedProperty(Object bean, String name, Object value)
  • PropertyUtils.setMappedProperty(Object bean, String name, String key, Object value)

例子:

Employee employee = ...;
    Address address = ...;
    PropertyUtils.setMappedProperty(employee, "address(home)", address);

    Employee employee = ...;
    Address address = ...;
    PropertyUtils.setMappedProperty(employee, "address", "home", address);

Nested Property Access(嵌套属性访问方式)

如果你的属性也是一个对象,你想访问属性对象的属性时,该怎么访问呢?

或许,用标准的java技术直接访问这个属性,会写成这样:

String city = employee.getAddress("home").getCity();

用PropertyUtils类的如下apis:

  • PropertyUtils.getNestedProperty(Object bean, String name)
  • PropertyUtils.setNestedProperty(Object bean, String name, Object value)

我们可以这样啊来访问:

String city = (String)
      PropertyUtils.getNestedProperty(employee, "address(home).city");

为了方便,PropertyUtils提供了一般化的访问方式,可以访问任意嵌套,sample,indexed,mapped类型的属性

  • PropertyUtils.getProperty(Object bean, String name)
  • PropertyUtils.setProperty(Object bean, String name, Object value)

例子:

Employee employee = ...;
    String city = (String) PropertyUtils.getProperty(employee,
      "subordinate[3].address(home).city");

Dynamic Beans (DynaBeans)

背景

PropertyUtils如前所述被设计用来访问存在的class的属性的访问方式,而不是以任何方式修改他们。一个不同的动态属性访问案例是,当你有一套合适的动态属性,想用一个javabean来展示,但是你并不想真实的写出一个类文件。除了不必保存和创建一个单独的class文件,它的功能意味着你可以处理这样一些情况,在这种情况下,你所关心的属性值是动态决定的。(比如sql语句查询出来的结果)。

为了支持这种情况,BeanUtils提供了DynaBean接口。通过实现他的接口方法可以实现他。并且与DynaClass 接口相关联。DynaClass 接口 定义了一个特定的DynaBean的组的属性。就像java.lang.Class定义了所有的javabean实例的属性一样。

据个例子:

如果上面例子中的Employee是DynaBean的实现。那么我们可以这样来访问它的属性。

DynaBean employee = ...; // Details depend on which
                             // DynaBean implementation you use
    String firstName = (String) employee.get("firstName");
    Address homeAddress = (Address) employee.get("address", "home");
    Object subordinate = employee.get("subordinate", 2);
注意:PropertyUtils的属性getter和setter方法知道如何访问DynaBean的属性。因此,你可以把你的应用中的所有属性访问方式都用PropertyUtils APIs。这样你就可以不用事先考虑某个特定bean到底是如何实现的。

因为 DynaBean 和 DynaClass都是接口,他们需要频繁的,很多不同场合地被实现。下面的章节提供了一些标准的beanutils包,当然,如果不符合您的要求,您也可以自己去实现他们。

BasicDynaBean and BasicDynaClass

  BasicDynaBean和BasicDynaClass提供了一套基本的动态属性性能,可以应用在你需要动态定义属性(是DynaProperty的实例)的时候。你需要先创建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);

注意,dynaBeanClass得引数(在BasicDynaClass的构造函数中)可以为空。在这种情况下,dynaClass.getDynaBeanClass的值仅仅只是BasicDynaBean的类(In this case, the value of dynaClass.getDynaBeanClass will just be the Class for BasicDynaBean)。

另外,你用DynaClass的newInstance()方法来实例化一个符合DynaClass的DynaBean实例,然后给他的属性赋初始值。(和你实例一个普通的javabean,然后赋值,是一样的)。

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

注意你可以这里的DynaBean类可以声明为DynaBean取代了BasicDynaBean。一般的,如果你使用DynaBeans,你不会在意DynaBeans的具体的实际实现---你只是在乎它是一个DynaBeans,而且可以用DynaBeans的apis.

 如上面所讲的,你可以传递一个DynaBean实例作为第一个引数给PropertyUtils访问和设置属性的方法,而且它会如你所愿的被解释---DynaBean 的动态属性可以被取回和修改。

ResultSetDynaClass (Wraps ResultSet in DynaBeans)

  一个很普通的DynaBean 的USER CASE就是用它来包装其他原始集合,这些集合不是以JAVABEAN的形式展示的。最常见的情况就是当你请求JDBC驱动查询SQL语句返回java.sql.ResultSet类型的记录的时候,BeanUtils提供了标准的机制来把每一行resultset转变为一个 DynaBean,参照下列:

Connection conn = ...;
  Statement stmt = conn.createStatement();
  ResultSet rs = stmt.executeQuery
    ("select account_id, name from customers");
  Iterator rows = (new ResultSetDynaClass(rs)).iterator();
  while (rows.hasNext()) {
    DynaBean row = (DynaBean) rows.next();
    System.out.println("Account number is " +
                       row.get("account_id") +
                       " and name is " + row.get("name"));
  }
  rs.close();
  stmt.close();

RowSetDynaClass (Disconnected ResultSet as DynaBeans)

尽管ResultSetDynaClass是一个用来展示sql查询的很好的技术(当成DynaBean),但是最大的问题就是在MVC的结构之中,我们需要离线的取出查询的所有数据,而ResultSetDynaClass必须保持和数据库相连。

RowSetDynaClass展示了解决这个问题的不同方法。当你构造这样的实例,那些原始的数据被复制到一系列in-memory 的DynaBeans来代表这些结果。这个技术的优势是,理所当然,你可以立即关闭ResultSet(和他相连的Statement),这些操作都可以在你处理被返回的数据之前。缺点就是,你需要为复制数据所需要的性能和内存买单,而且数据的大小还得让堆内存可以适合。在许多情况下(特别是WEB APPS),这种折衷是有益处的。

  额外的方便就是,RowSetDynaClass 被定义为java.io.Serializable的实现,因此它可以被序列化和反序列化。因此RowSetDynaClass展示了一种十分便利的方法来传输SQL结果到远程Java-based 客户端应用程序(比如APPLET).

 基本的RowSetDynaClass使用模式如下所示:

Connection conn = ...;  // Acquire connection from pool
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT ...");
    RowSetDynaClass rsdc = new RowSetDynaClass(rs);
    rs.close();
    stmt.close();
    ...;                    // Return connection to pool
    List rows = rsdc.getRows();
    ...;                   // Process the rows as desired

WrapDynaBean and WrapDynaClass

 下面的E文比较EASY,偶偷懒不翻了,

OK, you've tried the DynaBeans APIs and they are cool -- very simple get() and set() methods provide easy access to all of the dynamically defined simple, indexed, and mapped properties of your DynaBeans. You'd like to use the DynaBean APIs to access all of your beans, but you've got a bunch of existing standard JavaBeans classes to deal with as well. This is where the WrapDynaBean (and its associated WrapDynaClass) come into play. As the name implies, a WrapDynaBean is used to "wrap" the DynaBean APIs around an existing standard JavaBean class. To use it, simply create the wrapper like this:

MyBean bean = ...; DynaBean wrapper = new WrapDynaBean(bean); String firstName = wrapper.get("firstName");

Note that, although appropriate WrapDynaClass instances are created internally, you never need to deal with them.

Lazy DynaBeans(LazyDynaBean, LazyDynaMap and LazyDynaClass)

  你钟情于DynaBeans是因为有了它你不必对每个pojo来编码成一个class文件。这样能够实现的原因是因为lazy--延迟加载。是以下的一些特性使得DynaBeans可以lazy:

  •   Lazy property addition (lazy属性添加)---lazy beans 使用 实现了MutableDynaClass 接口的DynaClass,DynaClass提供了添加和删除属性的能力。当set方法调用的时候,Lazy beans 利用这个特性来自动添加DynaClass中没有的属性
  • Lazy List/Array growth(lazy list/array 增长)---如果一个索引化的属性没有足够的容量来容纳要设置的属性,那么List or Array 将会自动增长。
  • Lazy List/Array instantiation(Lazy List/Array实例化) ---如果一个索引化的属性并不存在,那么他将会调用 DynaBean的indexed property getter/setter methods(比如 get(name, index) or set(name, index, value))返回一个List 或者一个Array实例。如果一个索引化的属性没有在DynaClass中被定义,那么他将会被自动添加而且生成一个默认的list实现的实例。
  • Lazy Map instantiation-------if a mapped property doesn't exist then calling the DynaBean's mapped property getter/setter methods (i.e. get(name, key) or set(name, key, value)) results in a new Map being instantiated. If the mapped property has not been defined in the DynaClass then it is automatically added and a default Map implementation instantiated.
  • Lazy Bean instantiation -------如果一个DynaClass 中的属性被定义成DynaBean 或者普通的bean,但是这个属性并不在DynaBean中存在,那么LazyDynaBean将会采用默认的empty constructor来实例化这个 bean

LazyDynaBean

标准lazy bean 的实现。默认和实现了MutableDynaClass接口的LazyDynaClass相关联---尽管他可以和MutableDynaClass的任何实现一起使用。例子如下:

 DynaBean dynaBean = new LazyDynaBean();

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

    dynaBean.set("customer", "title", "Mr");      // mapped
    dynaBean.set("customer", "surname", "Smith"); // mapped

    dynaBean.set("address", 0, addressLine1);     // indexed
    dynaBean.set("address", 1, addressLine2);     // indexed
    dynaBean.set("address", 2, addressLine3);     // indexed

LazyDynaMap

light wieght (轻量级)DynaBean facade to a Map with all the usual lazy features。之所以是轻量级,是因为他没有和一个包含所有属性的DynaClass相关连。事实上,他亲自实现了DynaClass。一个LazyDynaMap可以用来包装一个存在的map,也可以自己去实例化一个Map实例

例如: 

If you need a new Map then to use....

DynaBean dynaBean = new LazyDynaMap(); // create DynaBean dynaBean.set("foo", "bar"); // simple dynaBean.set("customer", "title", "Mr"); // mapped dynaBean.set("address", 0, addressLine1); // indexed Map myMap = dynaBean.getMap() // retrieve the Map

or to use with an existing Map ....

Map myMap = .... // exisitng Map DynaBean dynaBean = new LazyDynaMap(myMap); // wrap Map in DynaBean dynaBean.set("foo", "bar");

LazyDynaClass

继承BasicDynaClass并实现MutableDynaClass接口。

Either create a LazyDynaClass first... MutableDynaClass dynaClass = new LazyDynaClass(); // create DynaClass dynaClass.add("amount", java.lang.Integer.class); // add property dynaClass.add("orders", OrderBean[].class); // add indexed property dynaClass.add("orders", java.util.TreeMapp.class); // add mapped property DynaBean dynaBean = new LazyDynaBean(dynaClass); // Create DynaBean with associated DynaClass

or create a LazyDynaBean and get the DynaClass... DynaBean dynaBean = new LazyDynaBean(); // Create LazyDynaBean MutableDynaClass dynaClass = (MutableDynaClass)dynaBean.getDynaClass(); // get DynaClass dynaClass.add("amount", java.lang.Integer.class); // add property dynaClass.add("myBeans", myPackage.MyBean[].class); // add 'array' indexed property dynaClass.add("myMap", java.util.TreeMapp.class); // add mapped property

 注意:

MutableDynaClass 有一种受限(Restricted)属性。When the DynaClass is restricted ,no properties can be added or removed from the DynaClass. Neither the LazyDynaBean or LazyDynaMap will add properties automatically if the DynaClass is restricted.

关于Converters和Collections方面的知识本文不做翻译。

posted on 2006-05-25 23:08 binge 阅读(890) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航: