天天看點

(轉)BeanUtils簡介

轉自 http://robbin82.spaces.live.com/Blog/cns!1pe1612S2VeLQWwufbzqRbNQ!114.entry

這個元件的全稱是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 需要一下一些包來運作:

  • Collections Package (Jakarta Commons), version 1.0 or later
  • Logging Package (Jakarta Commons), version 1.0 or later

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方面的知識本文不做翻譯。

 有不同意見和看法可以和作者聯系。[email protected]