這個組件的全稱是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方面的知識本文不做翻譯。 |