您的位置:首页 > 编程语言 > Java开发

BeanUtils

2014-01-08 23:31 344 查看
目录

一概论

1 背景
Javabean的名字来自一个Java API,就是一组规范。规范的目的在于简化编写易于理解的Java类。
符合javabean设计模式的类,JavaBean提供Java的自省能力。
也可以使得Java开发工具具有更易操作性,显示也更符合逻辑。
JavaBean规范
(1) JavaBean类必须Public,公共的无参构造函数(目的不知道类名的情况下可以动态创建实例)
如 String className = ...;
Class beanClass = Class.forName(className);
Object beanInstance = beanClass.newInstance();
(2) JavaBean 公共字段 一般眼提供公共的getter/setter属性
属性名称前缀set/get + 字段名 一般要符合驼峰命名法
(3) getter属性返回的类型 要和 setter 方法的参数类型一致
(4) 以上所述都是标准的JavaBean 但有时也可以不完全遵循这些规则



2 标准JavaBea



如上所述,在事先了解所要操作的Bean前提下,标准的JavaBean使得非常方便通过属性访问和修改段,
但更多的应用场景是在不了解Bean和字段的情况,此时改怎么访问呢?。

Java中提供类java.beans.Introspector类和反射功能仍可以进行访问。

Student student = new Student();
// 通过Introspector 获取类的BeanInfo信息
BeanInfo beanInfo = Introspector.getBeanInfo(student.getClass());
// 属性描述对象 用来描述 JavaBean的一个字段和一对getter/setter属性访问器
PropertyDescriptor[] propertyDes = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor o : propertyDes){
System.out.println(String.format("WriteMethod: %s", o.getWriteMethod()));
if("sex".equals(o.getName())){
Method method = o.getReadMethod();
Object value = method.invoke(student, null);
System.out.println("value: "+value);
}
}

但是这样的API用起来比较麻烦,同时还必须了解一些类底层的结构。
而BeanUtils包就是在这样的背景下出现的。
BeanUtils API 的目的在于简化动态访问JavaBean的属性。
属性类型大致可以分为三类 其中一些是标准JavaBean支持 另外一些则是BeanUtils包支持。
Simple : Java中八大基本类型 String 以及自定义类 引用Jar包中的类。
Indexed: 下标类型就是可以通过整数进行访问 数组 List集合
Mapped: Map如HashMap ? HashSet 是否支持

2.2 Basic Property Access
访问和修改Simple类型的属性可以通过一下方法
Employee employee = ...;
String firstName = (String)
PropertyUtils.getSimpleProperty(employee, "firstName");
PropertyUtils.setSimpleProperty(employee, "firstName", firstName);
访问和修改Indexed类型的属性有两种方式
PropertyUtils.getIndexedProperty(Object,String)
PropertyUtils.getIndexedProperty(Object,String,int)
PropertyUtils.setIndexedProperty(Object,String,Object)
PropertyUtils.setIndexedProperty(Object,String,int,Object)

int 只能是非负整数 如果需要动态的计算要访问的下标值则可以运用表达式
如:表达式中要用[]
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);
访问Map类型 和访问Indexed类型相似也有两种方式(注意:表达式用())
PropertyUtils.getMappedProperty(Object,String)
第一个参数 Bean 第二参数是指 字段名 第三个参数是指key
PropertyUtils.getMappedProperty(Object,String,String)
PropertyUtils.setMappedProperty(Object,String,Object)
PropertyUtils.setMappedProperty(Object,String,String,Object)

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

2.3 嵌套属性的访问
如果我们要访问的属性是一个对象 而我们要访问对象的某一属性
在JavaBean 要访问很简单如下
String city = employee.getAddress("home").getCity();

在BeanUtils中访问方式如下
PropertyUtils.getNestedProperty(Object,String)
PropertyUtils.setNestedProperty(Object,String,Object)
String city = (String)
PropertyUtils.getNestedProperty(employee, "address(home).city");

当然也可以更复杂
Employee employee = ...;
String city = (String) PropertyUtils.getProperty(employee,
"subordinate[3].address(home).city");
2.4 Java内省机制
如上所述,BeanUtils根据JavaBean的规范来判断具体Bean属性的合法性。因此对应所有的类都可以直接使用BeanUtils中所提供的功能。
但在开发过程中有时JavaBean 并不会遵循这些规则。从版本1.9开始BeanUtils只是Java的内省机制。
而这个机制的关键就在与BeanIntrospector接口。实现这个接口的对象这个对象可以为每一个JavaBean符合规范的属性生成一个
PropertyDescriptor 对象
public static void bean() throws Exception {

Student student = new Student();
// 通过Introspector 获取类的BeanInfo信息
BeanInfo beanInfo = Introspector.getBeanInfo(student.getClass());
/* A PropertyDescriptor describes one property that a Java Bean exports via a pair of accessor methods.
* 属性描述对象 用来描述 JavaBean的一个字段和一对getter/setter属性访问器
*/
PropertyDescriptor[] propertyDes = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor o : propertyDes){
System.out.println(String.format("DisplayName: %s", o.getDisplayName()));
System.out.println(String.format("Name: %s", o.getName()));
System.out.println(String.format("PropertyEditorClass: %s", o.getPropertyEditorClass()));
System.out.println(String.format("PropertyType: %s", o.getPropertyType()));
System.out.println(String.format("ReadMethod: %s", o.getReadMethod()));
System.out.println(String.format("WriteMethod: %s", o.getWriteMethod()));
if("sex".equals(o.getName())){
Method method = o.getReadMethod();
Object value = method.invoke(student, null);
System.out.println("value: "+value);
}
}
}
3 动态Bean(DynaBeans)
3.1 产生的背景
提供PropertyUtils的目的在于动态访问JavaBean属性,但却并不能修改这个Bean结构。另外一种应用场合只是想根据一组动态的属性集合来作为JavaBean
但又不想专门创建一个Java类来呈现这些属性,也就是说要求能够处理一些动态属性集合。
比如SQL语句执行的结果集Select id,code from sys_codeinfo 这条语句会得到 属性Id code 集合
我想处理这个结果集合 用如下动态Bean会比创建一个标准的JavaBean更方便
DynaBean codeBean= ...; // Details depend on which
// DynaBean implementation you use
String id= (String) codeBean.get("id");
String code = codeBean.get("code");
Object subordinate = employee.get("subordinate", 2);
NOTE:PropertyUtils 也能够直接通过get /set 方法访问动态bean属性
因为DynaBean 和 DynaClass 是接口,所以可以有很多实现类。现下面介绍集中BeanUtils包中的实现类
BasicDynaBean and BasicDynaClass
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);
Note that the 'dynaBeanClass' argument (in the constructor of BasicDynaClass) can have the value of null. In this case, the value of dynaClass.getDynaBeanClass will just be the Class for BasicDynaBean.
Next, you use the newInstance() method of this DynaClass to create new DynaBean instances that conform to this DynaClass, and populate its initial property values (much as you would instantiate a new standard JavaBean and then call its property setters):
DynaBean employee = dynaClass.newInstance();
employee.set("address", new HashMap());
employee.set("subordinate", new mypackage.Employee[0]);
employee.set("firstName", "Fred");
employee.set("lastName", "Flintstone");

3.3 ResultSetDynaClass (Wraps ResultSet in DynaBeans)
加入我们只关注一组属性集合如JDBC执行的结果 要想实现一个对应的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();
3.4 RowSetDynaClass (Disconnected ResultSet as DynaBeans)
从名称上便可知道这个和上面ResultSetDynaClass 这个类似 可以用于包装JDBC结果集合
区别在于:
上面的在操作结果集合的时候 链接不能关闭 当我们这个结果集合用于MVC中的View展示的时候
这个链接就不能关闭,因此造成资源无谓的占用和浪费。
RowSetDynaClass这个结果集合最大的不同在当查询出结果集合之后会保存在内存中,操作之前就可以把链接关闭。
另外 RowSetDynaClass被定义为实现了 java.io.Serializable所以便于能够进行序列化和反序列化 便于网络传输
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
3.5 WrapDynaBean and WrapDynaClass
当我们有一个JavaBean对象时如何创建对应的动态Bean
MyBean bean = ...;
DynaBean wrapper = new WrapDynaBean(bean);
String firstName = wrapper.get("firstName");
3.6 Lazy DynaBean
这种动态的Bean的最大特点是可以自动添加 属性
如 set 获取某一个属性 如果没有这个属性则新增
分为三种
1 LazyDynaBean
2 LazyDynaMap
3 LazyDynaList
4 LazyDynaClass
具有如下行为:
LazyProperty addition LazyDynaClass实现 MutableDynaClass 接口,这个接口提供添加和移出属性的功能
利用此功能当调用set方法时 如果没有对应的属性则 添加
Lazy List/Array growth - If an indexed property is not large enough to accomodate the index being set then the List or Array is automatically grown so that it is

?Lazy List/Array instantiation - if an indexed property doesn't exist then calling the DynaBean's indexed property getter/setter methods (i.e. get(name, index) or set(name, index, value)) results in either a new List or Array being instantiated. If the indexed property has not been defined in the DynaClass then it is automatically added and a default List implementation instantiated.
?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 - if a property is defined in the DynaClass as a DynaBean or regular bean and doesn't exist in the DynaBean then LazyDynaBean wiill try to instantiate the bean using a default empty constructor.
1. LazyDynaBean 关联 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
2. LazyDynaMap is a light weight DynaBean facade to a Map with all the usual lazy features.
Its light weight because it doesn't have an associated DynaClass containing all the properties.
In fact it actually implements the DynaClass interface itself (and MutableDynaClass)
and derives all the DynaClass information from the actual contents of the Map.
A LazyDynaMap can be created around an existing Map or can instantiate its own Map.
After any DynaBean processing has finished the Map can be retrieved and the DynaBean facade discarded.
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

Map myMap = .... // exisitng Map
DynaBean dynaBean = new LazyDynaMap(myMap); // wrap Map in DynaBean
dynaBean.set("foo", "bar"); // set properties
3. LazyDynaList is lazy list for DynaBeans java.util.Map's or POJO beans. See the Javadoc for more details and example usage.
4. LazyDynaClass extends BasicDynaClass and implements the MutableDynaClass interface. It can be used with other DynaBean implementations, but it is the default DynaClass used by LazyDynaBean. When using the LazyDynaBean there may be no need to have anything to do with the DynaClass. However sometimes there is a requirement to set up the DynaClass first - perhaps to define the type of array for an indexed property, or if using the DynaBean in restricted mode (see note below) is required. Doing so is straight forward...
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
NOTE: One feature of MutableDynaClass is that it has a Restricted property.
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.
4.2 数据格式转换
到目前为止,JavaBean属性都是已经知道的,我们可以直接转换。我们如何让程序自动完成这个类型转换的功能呢?
BeanUtils包下已经提供了很多这样的实现。String和基本类型的转换BeanUtils已经帮我们完成。
HttpServletRequest request = ...;
MyBean bean = ...;
HashMap map = new HashMap();
Enumeration names = request.getParameterNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
map.put(name, request.getParameterValues(name));
}
BeanUtils.populate(bean, map);

4.3 自定义转换类
ConvertUtils 提供注册自定义转换类(String---> Object),一旦为某个类型注册了自定义转换器,
那么这个转换器将会被BeanUtils包种所有的方法所使用
自定义转化器: 要实现Convert接口 重写convert(Class<T> type,Object value)方法
type: JavaBean需要转换的数据类型
value: 传入需要转换的值
例子:
转换器:
public class DateConverter implements Converter{
public Date convert(Class type, Object value) {
// TODO Auto-generated method stub
if(value instanceof String){
DateFormat formate = new SimpleDateFormat("yyy-MM-dd");

return formate.parse((String)value);

}
if(value instanceof Date){
DateFormat formate = new SimpleDateFormat("yyy-MM-dd");

return formate.parse(formate.format((Date)value));

}
return null;
}

public static void main(String[] args) throws Exception {
// Date.class 为Date类型注册一个转换器
ConvertUtils.register(new DateConverter(), Date.class);
Student student = new Student();
//BeanUtils.setProperty(student, "date", "2013-12-30");
BeanUtils.setProperty(student, "date", new Date());
System.out.println(student.getDate());
}

4.4 Locale Aware Conversions

5 静态实用工具类
到目前为止,以上所用到的都是静态工具类(BeanUtils, ConvertUtils and PropertyUtils).
这些工具最大的特点就是使用方便,但缺点是 会导致所有的线程共用了一样的转换器和缓存。
在实际种并不会出现这样的情况 这些静态工具类底层是通过对应类来实现操作的。
而每一个类都是单例模式一个线程只对应一个实例
BeanUtils BeanUtilsBean
ConvertUtils ConvertUtilsBean
PropertyUtils PropertyUtilsBean
6 集合
6.1 比较器Bean 可以对JiheBean对象进行比较
6.2 操作结合Bean对象 需要实现Closure
如 BeanPropertyValueChangeClosure 可以设定集合中某一个字段的值
// create the closure
BeanPropertyValueChangeClosure closure =
new BeanPropertyValueChangeClosure( "activeEmployee", Boolean.TRUE );
// update the Collection
CollectionUtils.forAllDo( peopleCollection, closure );
6.3 查询和过滤集合 实现这个 Predicate 接口 更多信息看API
如创建一个过滤
BeanPropertyValueEqualsPredicate predicate =
new BeanPropertyValueEqualsPredicate("activeEmployee", Boolean.FALSE);
// filter the Collection
CollectionUtils.filter(peopleCollection, predicate);
过滤字段activeEmployee 为false的数据
6.4 获取结合中Bean某一个属性的集合 实现接口Transformer
例子: 查找所有的城市
// create the transformer
BeanToPropertyValueTransformer transformer = new BeanToPropertyValueTransformer( "person.address.city" );
// transform the Collection
Collection peoplesCities = CollectionUtils.collect( peopleCollection, transformer

7 经常出现的问题
为什么BeanUtils不能查找Bean的方法
因为BeanUtils 更多是基于JavaBean的内省机制而非反射,这就意味着它更利于查找规范的属性信息
1 一个Bean的get/set属性只能有一个 也不允许覆盖:
2 java.beans.Introspector 查找BeanInfo过于宽泛,如果出现两个类名一样的Bean
则从缓存中查找已经有的BeanInfo.典型就是引用的第三方jar包
所以这个时候唯一的解决办法就是创建自己的BeanInfo
如何对一个集合进行排序呢 详细信息
import org.apache.commons.collections.comparators.ComparableComparator;
import org.apache.commons.collections.comparators.ReverseComparator;
import org.apache.commons.beanutils.BeanComparator;
...
BeanComparator reversedNaturalOrderBeanComparator
= new BeanComparator("propertyName", new ReverseComparator(new ComparableComparator()));
Collections.sort(myList, reversedNaturalOrderBeanComparator);
其中BeanUtils包中有PropertyUtils类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 编程 BeanUtils