Common-BeanUtils 使用
BeanUtils 介紹
???????所謂 BeanUtils 為何要開發呢, 每個工程師或許在寫 JavaBean 的時候, 都會乖乖地去寫 getters 和 setters, 就是 getXXX() 及 setXXX() methods, 但是當你的 object 是動態產生的, 也許是用檔案, 也許是其它原因, 那你該如何去存取數據呢 !!幾個例子你可能會用到 BeanUtils, 當然, 這是已經存在的項目了。
- BSF: Script Language 和 Java Object Model 之間
- Velocity/ JSP: 使用 template 建立相似的網頁
- jakarta taglibs/ Struts/ Cocoon: 建立自己特殊的 Tag Libraries for JSP 或 XSP
- ant build.xml/ tomcat server.xml: XML-based 的 設定檔案 ( configuration resources )
???????你大可以使用 java api 中的 java.lang.reflect 及 java.beans 來達到這些數據交換 ~~ 不過呢, 難度有點高 ,但是, BeanUtils 將會減低你開發的時間 !!目前最新的 stable 版本為 1.9.3,下載地址如下:
- commons-beanutils-1.9.3.jar;
- commons-logging.jar;
???????BeanUtils 的 Java API 主要的 package 總共四項:
???????1. org.apache.commons.beanutils;?
???????2. org.apache.commons.beanutils.converters;?
???????3. org.apache.commons.beanutils.locale;?
???????4. org.apache.commons.beanutils.locale.converters;
???后三個包主要是用于數據的轉換,圍繞著一個 Converter 接口,該接口只有一個方法:java.lang.Object convert(java.lang.Class type, java.lang.Object value) ,用于將一個 value 轉換成另一個類型為 type 的 Object。在將字符串類型的數據轉換成日期類型時會使用此方法。
測試用的Bean
???????在開始所有的測試之前,我寫了一個簡單的 Bean,以便于測試,代碼如下:
package com.study.java.domain;import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map;/** * @Name: User * @Description: JavaBean-User信息類 * @Author: XXX * @CreateDate: XXX * @Version: V1.0*/ public class User implements Serializable {private static final long serialVersionUID = 5210726534179789239L;private String name ;private int age ; private boolean gender ; //性別:trye-男,false-女private Date birthday ; //生日private String[] hobbies ; //愛好private String address ; private List<String> strong ; //特長private Map<String, String> fault ; //缺點private Student Student ; //內嵌學生對象public User() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public boolean isGender() {return gender;}public void setGender(boolean gender) {this.gender = gender;}public Date getBirthday() {return birthday;}public void setBirthday(Date birthday) {this.birthday = birthday;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String[] getHobbies() {return hobbies;}public void setHobbies(String[] hobbies) {this.hobbies = hobbies;}public List<String> getStrong() {return strong;}public void setStrong(List<String> strong) {this.strong = strong;}public Map<String, String> getFault() {return fault;}public void setFault(Map<String, String> fault) {this.fault = fault;}public Student getStudent() {return Student;}public void setStudent(Student student) {Student = student;}}Commons-Beanutils工具詳解
BeanUtils的使用
???????Beanutils工具在使用時幾乎只用到以下幾個方法,其中一個方法通常情況下都是使用匿名內部類,用來注冊日期類型轉換器,以將字符串類型的數據轉換成指定格式的日期類型。
-
BeanUtils.setProperty(bean, name, value):為指定bean實例的屬性設值,等同于bean.setXXX()方法;其中bean是指你將要設置的對象,name指的是將要設置的屬性(寫成”屬性名”),value(想要設置的屬性值);
-
BeanUtils.copyProperties(bean, name, value):與上邊的setProperty方法功能相同;
-
ConvertUtils.register(Converter converter , Class clazz):類型轉換器注冊方法,當需要將String數據轉換成引用數據類型(自定義數據類型時,例如Date類型),需要使用此方法實現轉換;
-
BeanUtils.populate(bean,Map):將Map集合中的數據注入到JavaBean的屬性中去,其中Map中的key必須與目標對象中的屬性名相同,否則不能實現拷貝;
-
BeanUtils.copyProperties(newObject,oldObject):實現對象之間的拷貝。
說明:自定義數據類型使用BeanUtils工具時必須具備的條件
???????自定義數據類型使用BeanUtils工具時,本身必須具備getter和setter方法,因為BeanUtils工具本身也是一種內省的實現方法,所以也是借助于底層的getter和setter方法進行轉換的。
/*** @Name: testSetProperty* @Description: * 1、設置Bean對象的單個屬性* public static void setProperty(Object bean, String name, Object value)* =* public static void copyProperty(Object bean, String name, Object value)* @Author: XXX* @Version: V1.0* @CreateDate: XXX* @Parameters: @throws Exception* @Return: void*/@Testpublic void testSetProperty() throws Exception {System.out.println(user);//設置String類型的屬性BeanUtils.setProperty(user, "user", "李四") ;//設置int類型的屬性BeanUtils.setProperty(user, "age", 25) ;//設置boolean類型的屬性BeanUtils.setProperty(user, "gender", "false") ;//設置Date類型的屬性//異常信息:DateConverter does not support default String to 'Date' conversion.//注冊String->日期類型轉換器ConvertUtils.register(new Converter() {@Overridepublic Object convert(Class type, Object value) {if(type != Date.class) {return null ;}if(value == null && "".equals(value.toString().trim())) {return null ;}SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;Date date = null ;try {date = sdf.parse((String) value) ;} catch (ParseException e) {throw new RuntimeException(e) ;}return date;}}, Date.class) ;BeanUtils.setProperty(user, "birthday", "2016-92-11") ;//設置數組類型的屬性String[] hobbies = {"11", "22", "33"} ;BeanUtils.setProperty(user, "hobbies", hobbies) ;//設置List、Set、Map、內嵌對象等...System.out.println(user);} /*** @Name: testPupulate* @Description: * 2、將Map<String, Object>集合中的內容設置到JavaBean的屬性上* 說明:* Map的key:必須與JavaBean的屬性名稱相同;* Map的value:注入到JavaBean的屬性;* 備注:* 此方法經常用于將表單提交的參數設置到表單Bean對象中* @Author: XXX* @Version: V1.0* @CreateDate: XXX* @Parameters: * @Return: void*/@Testpublic void testPupulate() throws Exception {Map<String, Object> map = new HashMap<String, Object>() ;//字符串->字符串屬性map.put("name", "李大魁") ;//字符串->整型屬性map.put("age", "25") ;//字符串->布爾型屬性map.put("gender", "false") ;//字符串->日期類型屬性map.put("birthday", "2016-12-10") ;//字符串->數組類型屬性String[] hobbies = {"11", "22", "33"} ;map.put("hobbies", hobbies) ;//字符串->List集合屬性List<String> strong = new ArrayList<String>() ;strong.add("1111") ;strong.add("22222") ;strong.add("33333") ;map.put("strong", strong) ;//字符串->Map集合屬性Map<String, String> fault = new HashMap<String, String>() ;fault.put("學習222", "偷懶,反應慢") ;fault.put("感情222", "磨嘰,不勇敢") ;map.put("fault", fault) ;//字符串->內嵌對象屬性Student student = new Student("三思", "25") ;map.put("student", student) ;User user = new User() ;//注冊日期類型轉換器ConvertUtils.register(new Converter() {@Overridepublic Object convert(Class type, Object value) {if(type != Date.class) {return null ;}if(value == null || "".equals(value.toString().trim())) {return null ;}SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;Date date = null ;try {date = sdf.parse((String) value);} catch (ParseException e) {e.printStackTrace();}return date;}}, Date.class) ;//將Map集合注入到JavaBeanBeanUtils.populate(user, map) ;System.out.println(user);}???????下邊是我在開發中常用到的表單數據轉換工具類WebUtils:
package com.study.java.utils;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map;import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter;/** * @Name: WebUtils * @Description: Web工具類 * @Author: XXX * @CreateDate: XXX * @Version: V1.0*/ public class WebUtils {private static final String FORMAT = "yyyy-MM-dd" ;/*** @Name: fillBean* @Description: 將Map集合中的數據封裝到JavaBean* 說明:* Map的key:與屬性名稱保持一致;* Map的value:設置為屬性值;* @Author: XXX* @Version: V1.0* @CreateDate: XXX* @Parameters: @param map* @Parameters: @param clazz* @Return: T*/public static <T> T fillBean(Map<String, Object> map, Class<T> clazz) {T bean = null ;try {bean = clazz.newInstance() ; //注冊日期類型轉換器ConvertUtils.register(new Converter() {@Overridepublic Object convert(Class type, Object value) {if(type != Date.class) {return null ;} if(value == null || "".equals(value.toString().trim())) {return null ;}SimpleDateFormat sdf = new SimpleDateFormat(FORMAT) ;Date date = null ;try {date = sdf.parse((String) value) ;} catch (ParseException e) {e.printStackTrace();}return date;}}, Date.class) ;//將Map集合中的數據封裝到JavaBeanBeanUtils.populate(bean, map) ;} catch (Exception e) {throw new RuntimeException(e) ;}return bean ;}/*** @Name: copyProperties* @Description: 實現JavaBean對象之間的屬性復制* 說明:原對象與目標對象內的屬性名稱必須相同* @Author: XXX* @Version: V1.0* @CreateDate: XXX* @Parameters: @param tClazz* @Parameters: @param oClazz* @Return: void*/public static <T, O> void copyProperties(Class<T> tClazz, Class<O> oClazz) {try {T target = tClazz.newInstance() ;O origin = oClazz.newInstance() ;//注冊日期類型轉換器ConvertUtils.register(new Converter() {@Overridepublic Object convert(Class type, Object value) {if(type != Date.class) {return null ;} if(value == null || "".equals(value.toString().trim())) {return null ;}SimpleDateFormat sdf = new SimpleDateFormat(FORMAT) ;Date date = null ;try {date = sdf.parse((String) value) ;} catch (ParseException e) {e.printStackTrace();}return date;}}, Date.class) ;//將源對象內的屬性值復制到目標對象的屬性上BeanUtils.copyProperties(target, origin) ;} catch (Exception e) {throw new RuntimeException(e) ;}} }PropertyUtils的使用
???????這個類和 BeanUtils 類很多的方法在參數上都是相同的,但返回值不同。BeanUtils 著重于”Bean”,返回值通常是 String,而 PropertyUtils 著重于屬性,它的返回值通常是 Object。
???????在 PropertyUtils 中會區分為三種 method 狀態:
- Simple:如果你是用到 primitive 語法, 如 int, String 或其它自行開發的 objects 等等, 只需要單一的對象就可以取得數據;
- 1
- Indexed:如果你是用到 Collection 或 List 實作出來的 objects , 只需要使用一個 index 數值就可以取得數據的型態;
- Mapped:如果你是用到 Map 延伸出來的 objects , 只需要使用一個 key 值就可以取得數據;
但是如果你是巢狀(nested)的數據結構, 你該如何取得你要的數據呢?
PropertyUtils.getNestedProperty(Object bean, String name); PropertyUtils.setNestedProperty(Object bean, String name, Object value);只需要簡單地使用 “.”, 就可以得到你要的數據了。
ConstructorUtils的使用
???????這個類中的方法主要分成兩種,一種是得到構造方法,一種是創建對象。事實上多數時候得到構造方法的目的就是創建對象,這里只介紹一下創建對象。
//根據一個 java.lang.Class 以及相應的構造方法的參數,創建一個對象 static java.lang.Object ConstructorUtils.invokeConstructor(java.lang.Class klass, java.lang.Object[] args) ;???????測試使用的JavaBean:
package com.study.java.domain;import java.io.Serializable;/** * @Name: Month * @Description: 測試JavaBean * @Author: XXX * @CreateDate: XXX * @Version: V1.0*/ public class Month implements Serializable {private static final long serialVersionUID = -3631848758000270789L;private String name ;private int value ;private int[] days = {11, 22, 33, 44, 55} ;public Month() {}public Month(String name, int value) {this.name = name;this.value = value;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public int[] getDays() {return days;}public void setDays(int[] days) {this.days = days;}}???????測試代碼:
Object obj=ConstructorUtils.invokeConstructor(Month.class, {new Integer(1), "Jan"}); Month month=(Month)obj; try { System.out.println(BeanUtils.getProperty(month,"value")); } catch (Exception e) {e.printStackTrace(); }???????如果輸出證明,構造方法的調用是成功的。?
???????如果需要強制指定構造方法的參數類型,可以這樣調用:
???????補充:創建對象還有一個方法:invokeExactConstructor,該方法對參數要求更加嚴格,傳遞進去的參數必須嚴格符合構造方法的參數列表。例如:
Object[] args={new Integer(1), "Jan"}; Class[] argsType={int.class, String.class}; Object obj = null ; //下面這句調用將不會成功,因為 args[0]的類型為 Integer,而不是 int //obj = ConstructorUtils.invokeExactConstructor(Month.class, args); //這一句就可以,因為 argsType 指定了類型。 obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType); Month month=(Month)obj; System.out.println(BeanUtils.getProperty(month,"value"));MethodUtils的使用說明
???????與 ConstructorUtils 類似,不過調用的時候,通常需要再指定一個 method name 的參數。
DynaClass/DynaBean
???????這似乎是BeanUtils中最有趣的部分之一了,很簡單,簡單到光看這兩個接口中的方法會不明白為什么要設計這兩個接口?不過看到ResultSetDynaClass后,就明白了。下面是java doc中的代碼:
ResultSet rs = ...; ResultSetDynaClass rsdc = new ResultSetDynaClass(rs); Iterator rows = rsdc.iterator(); while (rows.hasNext()) { DynaBean row = (DynaBean) rows.next(); ... process this row ... } rs.close();???????原來這是一個ResultSet的包裝器,ResultSetDynaClass實現了DynaClass,它的iterator方法返回一個ResultSetIterator,則是實現了DynaBean接口。在獲得一個DynaBean之后,我們就可以用:
DynaBean row = (DynaBean) rows.next(); System.out.println(row.get("field1")); //field1是其中一個字段的名字???????再看另一個類RowSetDynaClass的用法,代碼如下:
String driver="com.mysql.jdbc.Driver"; String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK"; String username="root"; String password=""; Connection con=null; PreparedStatement ps=null; ResultSet rs=null; try { Class.forName(driver).newInstance();con = DriverManager.getConnection(url); ps=con.prepareStatement("select * from forumlist");rs=ps.executeQuery(); //先打印一下,用于檢驗后面的結果。while(rs.next()){System.out.println(rs.getString("name"));}rs.beforeFirst();//這里必須用beforeFirst,因為RowSetDynaClass只從當前位置向前滾動 RowSetDynaClass rsdc = new RowSetDynaClass(rs); rs.close();ps.close(); List rows = rsdc.getRows();//返回一個標準的List,存放的是DynaBean for (int i = 0; i <rows.size(); i++) { DynaBean b=(DynaBean)rows.get(i); System.out.println(b.get("name")); } } catch (Exception e) {e.printStackTrace(); } finally {try {con.close();} catch (Exception e) {e.printStackTrace();} }???????是不是很有趣?封裝了ResultSet的數據,代價是占用內存。如果一個表有10萬條記錄,rsdc.getRows()就會返回10萬個記錄。
???????需要注意的是ResultSetDynaClass和RowSetDynaClass的不同之處:
???????1. ResultSetDynaClass是基于Iterator的,一次只返回一條記錄,而RowSetDynaClass是基于List的,一次性返回全部記錄。直接影響是在數據比較多時ResultSetDynaClass會比較的快速, 而RowSetDynaClass需要將ResultSet中的全部數據都讀出來(并存儲在其內部),會占用過多的內存,并且速度也會比較慢。
??????? 2. ResultSetDynaClass一次只處理一條記錄,在處理完成之前,ResultSet不可以關閉。
??????? 3. ResultSetIterator的next()方法返回的DynaBean其實是指向其內部的一個固定 對象,在每次next()之后,內部的值都會被改變。這樣做的目的是節約內存,如果你需要保存每次生成的DynaBean,就需要創建另一個DynaBean,并將數據復制過去,下面也是java doc中的代碼:
ArrayList results = new ArrayList(); // To hold copied list ResultSetDynaClass rsdc = ...; DynaProperty properties[] = rsdc.getDynaProperties(); BasicDynaClass bdc = new BasicDynaClass("foo", BasicDynaBean.class, rsdc.getDynaProperties()); Iterator rows = rsdc.iterator(); while (rows.hasNext()) { DynaBean oldRow = (DynaBean) rows.next();DynaBean newRow = bdc.newInstance();PropertyUtils.copyProperties(newRow, oldRow);results.add(newRow); }???????事實上DynaClass/DynaBean可以用于很多地方,存儲各種類型的數據。自己想吧。嘿嘿。
自定義的CustomRowSetDynaClass
???????兩年前寫過一個與RowSetDynaClass目標相同的類,不過多一個功能,就是分頁,只取需要的數據,這樣內存占用就會減少。先看一段代碼:
String driver="com.mysql.jdbc.Driver"; String url="jdbc:mysql://localhost/2hu?useUnicode=true&characterEncoding=GBK"; String username="root"; String password=""; Connection con=null; PreparedStatement ps=null; ResultSet rs=null; try { Class.forName(driver).newInstance();con = DriverManager.getConnection(url); ps=con.prepareStatement("select * from forumlist order by name"); rs=ps.executeQuery();/* while(rs.next()){ System.out.println(rs.getString("name")); }rs.beforeFirst();*///第二個參數表示第幾頁,第三個參數表示頁的大小CustomRowSetDynaClass rsdc = new CustomRowSetDynaClass(rs, 2, 5);//RowSetDynaClass rsdc = new RowSetDynaClass(rs); rs.close(); ps.close(); List rows = rsdc.getRows(); for (int i = 0; i <rows.size(); i++) { DynaBean b=(DynaBean)rows.get(i); System.out.println(b.get("name")); } } catch (Exception e) { e.printStackTrace(); }finally{try { con.close(); } catch (Exception e) {e.printStackTrace(); } }???????在這里用到了一個CustomRowSetDynaClass類,構造方法中增加了page和pageSize兩個參數,這樣,不管數據庫里有多少條記錄,最多只取pageSize條記錄,若pageSize==-1,則功能和RowSetDynaClass一樣。這在大多數情況下是適用的。該類的代碼如下:
package com.study.java.utils;import java.io.Serializable; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;import org.apache.commons.beanutils.BasicDynaBean; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.DynaClass; import org.apache.commons.beanutils.DynaProperty;/** * @Name: CustomRowSetDynaClass * @Description: 自定義CustomRowSetDynaClass * @Author: XXX * @CreateDate: XXX * @Version: V1.0*/ public class CustomRowSetDynaClass implements DynaClass, Serializable {protected boolean lowerCase = true;protected int page = 1;protected int pageSize = -1;protected DynaProperty properties[] = null;protected Map propertiesMap = new HashMap();protected List rows = new ArrayList();public CustomRowSetDynaClass(ResultSet resultSet) throws SQLException {this(resultSet, true) ;}public CustomRowSetDynaClass(ResultSet resultSet, boolean lowerCase) throws SQLException {this(resultSet, 1, -1, lowerCase) ;}public CustomRowSetDynaClass(ResultSet resultSet, int i, int j,boolean lowerCase) {if(resultSet ==null) {throw new NullPointerException() ;}this.lowerCase = lowerCase;this.page = page;this.pageSize = pageSize;try {introspect(resultSet);copy(resultSet);} catch (SQLException e) {e.printStackTrace();}}public CustomRowSetDynaClass(ResultSet resultSet, int page, int pageSize) throws SQLException {this(resultSet, page, pageSize, true);}public DynaProperty[] getDynaProperties() {return (properties);}public DynaProperty getDynaProperty(String name) {if (name == null) {throw new IllegalArgumentException("No property name specified");}return ((DynaProperty) propertiesMap.get(name));}public String getName() {return (this.getClass().getName());}public DynaBean newInstance() throws IllegalAccessException,InstantiationException {throw new UnsupportedOperationException("newInstance() not supported");}public List getRows() {return (this.rows);}protected void copy(ResultSet resultSet) throws SQLException {int abs = 0;int rowsCount = 0;int currentPageRows = 0;resultSet.last();rowsCount = resultSet.getRow();if (pageSize != -1) {int totalPages = (int) Math.ceil(((double) rowsCount) / pageSize);if (page > totalPages)page = totalPages;if (page < 1)page = 1;abs = (page - 1) * pageSize;} else {pageSize = rowsCount;if (abs == 0)resultSet.beforeFirst();elseresultSet.absolute(abs);while (resultSet.next() && ++currentPageRows <= pageSize) {DynaBean bean = new BasicDynaBean(this);for (int i = 0; i < properties.length; i++) {String name = properties[i].getName();bean.set(name, resultSet.getObject(name));}rows.add(bean);}}}protected void introspect(ResultSet resultSet) throws SQLException {ArrayList list = new ArrayList();ResultSetMetaData metadata = resultSet.getMetaData();int n = metadata.getColumnCount();for (int i = 1; i <= n; i++) { // JDBC is one-relative!DynaProperty dynaProperty = createDynaProperty(metadata, i);if (dynaProperty != null) {list.add(dynaProperty);}}} }總結
以上是生活随笔為你收集整理的Common-BeanUtils 使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 打印杨辉三角 - C语言实现
- 下一篇: 【新年好】为什么要 spring-ses