wang

xiaoxiao2026-09-12  8

1.BeanUtils介绍很多Java开发人员习惯于创建符合JavaBeans命名模式(属性的getter和setter)的Java类。然后直接访问这些方法,通过使用getXxx和setXxx方法。然而,有时需要动态的访问Java对象的属性。一些应用场景包括:与java对象模型交互的构建脚本语言(例如Bean Scripting Framework)构建用于web表示和相关应用的模型语言处理器(JSP或者Velocity)为JSP和XSP环境构建自定义标签(例如Jakarta标签库,Struts,Cocoon)解析基于XML的配置资源(例如Ant构建脚本,Web部署描述符,Tomcat的server.xml文件)Java语言提供了反射和自省API,包含在java.lang.reflect和java.beans包中。然而,这些API难于理解和使用,所以BeanUtils组件提供了简单易用的封装。0.简介Apache Commons项目是专注于开发可重用的Java组件。Apache Commons项目由三部分组成:Commons Proper - 可重用Java组件库。Commons Sandbox - Java组件开发工作空间。Commons Dormant - sandbox中不活跃的项目存储库。Apache Commons开发人员使每个组件尽可能少的依赖其他的lib,以便这些组件可以方便的部署,而不是需要将依赖的库依次部署。另外,Commons组件的接口尽可能的保持不变,这样可以在实现这些组件的时候针对接口进行开发,确保兼容性。本文主要介绍第一部分的项目,即可重用的稳定的Java组件库。由于库中的每个子项目都包含了特定的功能,所以本文不会对这些功能面面俱到的介绍,而是针对库中的每个子项目,尽可能的介绍其应用场景,以及列举典型的样例。

BeanUtils核心和模型:从1.7.0发布版本开始,都包含3个jar文件:commons-beanutils.jar - 包含了所有的内容commons-beanutils-core.jar - 不包含Bean Collections类commons-beanutils-bean-collections.jar - 只包含Bean Collections类common-beanutils.jar有对Commons Collections的一个可选的依赖。

关于Bean Collections:Bean collections是将BeanUtils和Commons Collections组合起来的一个库,用来提供对beans集合的service。以前发布一个BeanComparator类,其他的类是新发布的。Bean Collections依赖于Commons Collections。

2.项目依赖本项目依赖的库包括如下的内容:编译:commons-logging1.1.1commons-collections3.2.1test:commons-collections3.2.1junit3.8.1依赖关系树:* commons-beanutils:commons-beanutils:jar      o junit:junit:jar      o commons-collections:commons-collections:jar      o commons-collections:commons-collections-testframework:jar      o commons-logging:commons-logging:jar3.背景JavaBeans这个名字来自于Java语言中的一个组件框架API。开发符合JavaBeans设计模式的类可以使java开发人员更容易的理解其功能,同时使得基于JavaBeans的工具使用Java内省机制的功能来获取类的属性和方法。JavaBeans规范描述了Java类是否为JavaBean的特点。规范中要求的JavaBeans的特点有下面这些:类必须是public,提供public无参构造方法。这使得应用程序或者工具可以直接创建类的实例,而不是在创建实例之前先知道类的名字。作为无参构造方法的结果,必须独立的完成bean的初始化。一般通过定义bean的属性来完成。典型的,每个Bean属性都包含public的getter和setter方法来获取和设置属性,使用getXxx和setXxx来作为方法名。对于boolean型的属性,可以使用isXxx来代替getXxx。getter方法和setter方法中,getter方法的返回值类型要和setter方法的参数类型一致。并不要求对每个属性都设置getter和setter方法。例如如果属性是只读的,那么就不需要setter方法。只写的属性不常见。但是是允许的。同时,可以书写命名规则和JavaBeans的getter和setter不一致的类,标准的JavaBeans支持Java语言中的类,也支持BeanUtils包,允许在与Bean类关联的BeanInfo类中描述实际的方法名。JavaBeans规范中还描述了很多额外的设计模式,例如事件监听器,将JavaBeans集成到组件继承层次,以及其他一些超出BeanUtils范围的特性。4.标准JavaBeans如前所述,可以很方便的通过合适的getter方法来获取beans的属性。但是有的时候没有办法在使用之前知道类的名字,或者不知道该获取/设置什么属性。Java语言提供了java.beans.Introspector这样的类,它们可以在运行时检查java类并标识属性的getter和setter类。同时,反射功能还可以动态的调用这些方法。然而,这些API用起来比较困难,并且暴露了太多的不必要的细节给程序员。BeanUtils API用来在运行时动态的get和set bean的属性、访问对象的位置以及属性的名字,而不是在编译时。这些需求都可以通过PropertyUtils类的静态方法来满足。JavaBean支持的属性的类型可以分为三类,一些是标准规范支持,另一些只有BeanUtils支持。Simple:或者叫标量属性,包含一个单一的值,可以被获取或者设置。属性类型可以是原始类型或者简单对象,例如java.lang.String,或者更复杂一些的对象。Indexed:带索引的属性存书了一个有序的对象集合(相同类型的对象),可以通过非负整数作为索引来访问。也就是说,整个值可以通过数组来设置或者获取。作为对Javabeans的扩展,BeanUtils将类型为java.util.List的属性看作是索引的。Mapped:BeanUtils将类型为java.util.Map的属性看作是mapped类型的属性。可以通过字符串的key-value来访问和设置。可以通过PropertyUtils类来设置或者获取所有上述三种类型的属性。    * PropertyUtils.getSimpleProperty(Object bean, String name)    * PropertyUtils.setSimpleProperty(Object bean, String name, Object value)    * 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)    * 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 = ...;    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);    Employee employee = ...;    Address address = ...;    PropertyUtils.setMappedProperty(employee, "address(home)", address);通过使用属性名+[+index+]的方式来作为索引属性的属性名。    Employee employee = ...;    Address address = ...;    PropertyUtils.setMappedProperty(employee, "address", "home", address);

    Employee employee = ...;    int index = ...;    String name = "subordinate[" + index + "]";    Employee subordinate = (Employee)      PropertyUtils.getIndexedProperty(employee, name);通过属性名+(+key+})来作为mapped属性的属性名。    Employee employee = ...;    int index = ...;    Employee subordinate = (Employee)      PropertyUtils.getIndexedProperty(employee, "subordinate", index);

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

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

上面的例子中,都是直接获取bean的属性值,但是如果bean的属性是一个对象,而我们需要获取那个对象的属性呢?可以通过PropertyUtils类的getNestedProperty来获取。将属性名路径和属性名用.分隔符连接。例如我们要获取employee bean的home的address的city部分。Employee:Map<String,Address> address;Address:String city;    * PropertyUtils.getNestedProperty(Object bean, String name)    * PropertyUtils.setNestedProperty(Object bean, String name, Object value)

    String city = (String)      PropertyUtils.getNestedProperty(employee, "address(home).city");

为了方便使用,还提供了统一的获取和设置属性的方法:    * PropertyUtils.getProperty(Object bean, String name)    * PropertyUtils.setProperty(Object bean, String name, Object value)5.动态beans(DynaBeans)PropertyUtils类提供了动态获取/设置既存JavaBean类的属性,但是并不修改JavaBean类。另一种动态属性访问的方式是希望表示动态计算的JavaBean属性集,而不是去编写实际的Java类来表示这些属性。除了节省创建和维护单独的java类的工作之外,这个功能意味着可以可以处理动态决定的属性。(例如将SQL查询语句的结果作为JavaBeans来表示)。为了提供这个功能,BeanUtils提供了DynaBean接口以及关联的DynaClass接口,DynaClass接口定义了特定一组DynaBeans支持的属性,这和java.lang.Class定义的被所有JavaBean类支持的属性集相同。例如,Employee类可以实现DynaBean接口,而不是标准的JavaBean,此时可以通过下面的方式来访问其属性:    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);由于DynaBean和DynaClass都是接口,所以多次被实现,在不同的应用场景以及以不同的方式。BasicDynaBean和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);注意BasicDynaClass的dynaBeanClass参数可以设置为null,此时,dynaClass.getDynaBeanClass返回BasicDynaBean的Class。接下来就可以使用newInstance方法来创建DynaClass实例,并进行填充初始值。    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作为第一个对象提供给PropertyUtils类的方法。而在此过程中,并不需要创建独立的java源文件。

ResultSetDynaClass专门用于封装SQL SELECT语句的结果。但是ResultSet必须一致保持open的状态。这阻碍了在MVC结构(例如struts)中使用ResultSetDynaClass在model层和视图层之间的交互,因为无法确保ResultSet最后被关闭了。RowSetDynaClass用于解决上述问题。当创建RowSetDynaClass时,底层的数据被拷贝到内存中的DynaBeans集合中。该方式的好处在于可以在创建之后就关闭ResultSet。缺点自然就是性能上的损失,因为要将数据放在内存中。WrapDynaBean和WrapDynaClass提供了对标准JavaBeans的封装。    MyBean bean = ...;    DynaBean wrapper = new WrapDynaBean(bean);    String firstName = wrapper.get("firstName");Lazy DynaBeans提供了以下的特性:添加属性,增长List/Array长度,List/Array实例化,Map实例化,Bean实例化。Lazy DynaBeans包含以下几个类:LazyDynaBean,LazyDynaList,LazyDynaMap,LazyDynaClass。

6.类型转换有时数据的类型是已知的,或者可以通过Java的强制类型转换来完成,但是如果无法进行强制类型转换呢?BeanUtils提供了大量的API以及设计模式来执行类型转换工作。一个普遍的应用也是BeanUtils库开发的初衷就是将javax.servlet.HttpServletRequest中包含的请求参数转换为一个属性集JavaBean中。    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);BeanUtils类依赖于ConvertUtils类中定义的转换方法,来进行实际的转换工作,这些方法也可以直接使用。但是显式的使用ConvertUtils在已经是deprecated,作为替代,可以使用Converter接口的自定义实现。现在已经有很多转换实现类存在。7.集合org.apache.commons.beanutils.BeanComparator是一个Comparator接口的实现,用于比较具有相同属性值的beans。common-collections项目中的Closure接口封装了执行任意输入对象的代码块。commons-collections包含了允许Closures应用于Collection的代码。BeanPropertyValueChangeClosure是一个Closure,用于为特定的属性设置特定的值。一个典型的应用就是将集合中的所有bean的一个属性都设置特定的值。例如将整个集合的bean的activeEmployee属性设置为TRUE:    // create the closure    BeanPropertyValueChangeClosure closure =        new BeanPropertyValueChangeClosure( "activeEmployee", Boolean.TRUE );    // update the Collection    CollectionUtils.forAllDo( peopleCollection, closure );

commons-collections中的Predicate接口封装了对输入对象的判断,返回true或者false。commons-collections允许Predicates应用于集合的过滤。BeanPropertyValueEqualsPredicate是一个Predicate,用于过滤针对特定值的bean。例如过滤整个集合,查找activeEmployee的值是false的所有beans:    BeanPropertyValueEqualsPredicate predicate =        new BeanPropertyValueEqualsPredicate( "activeEmployee", Boolean.FALSE );    // filter the Collection    CollectionUtils.filter( peopleCollection, predicate );

commons-collections中的Transformer接口封装了将输入对象转换为输出对象。commons-collections允许使用Transformer来通过输入集合创建输出集合。BeanToPropertyTransformer是一个Transformer,用于将bean转换为它的属性的值。例如获取每个bean的person属性中的地址属性中的城市信息,输出到集合中:// create the transformer    BeanToPropertyValueTransformer transformer = new BeanToPropertyValueTransformer( "person.address.city" );    // transform the Collection    Collection peoplesCities = CollectionUtils.collect( peopleCollection, transformer );

8.总结

转载请注明原文地址: https://www.6miu.com/read-5052602.html

最新回复(0)