Common-BeanUtils研究心得

    xiaoxiao2021-03-25  104


    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 或 XSPant 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 */ @Test public 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() { @Override public 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 */ @Test public 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() { @Override public 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集合注入到JavaBean BeanUtils.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() { @Override public 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集合中的数据封装到JavaBean BeanUtils.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() { @Override public 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 等等, 只需要单一的对象就可以取得数据; PropertyUtils.getSimpleProperty(Object bean, String name); Indexed:如果你是用到 Collection 或 List 实作出来的 objects , 只需要使用一个 index 数值就可以取得数据的型态; 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); Mapped:如果你是用到 Map 延伸出来的 objects , 只需要使用一个 key 值就可以取得数据; 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);

    但是如果你是巢状(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(); }

           如果输出证明,构造方法的调用是成功的。        如果需要强制指定构造方法的参数类型,可以这样调用:

    Object[] args={new Integer(1), "Jan"}; Class[] argsType={int.class, String.class}; Object obj = null ; obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType); //argsType 指定了参数的类型 Month month=(Month)obj; System.out.println(BeanUtils.getProperty(month,"value"));

           补充:创建对象还有一个方法: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(); else resultSet.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); } } } }
    转载请注明原文地址: https://ju.6miu.com/read-16765.html

    最新回复(0)