commons-beanutils v1.9.2 apidoc翻译

文档原文地址:http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.2/apidocs/org/apache/commons/beanutils/package-summary.html


以下是个人的简单翻译:



1. Overview
    1.1 Background
        JavaBean这个名称来自于一个Java API,这个API用于Java语言的组件架构。编写符合JavaBean设计范式的Java类让开发者能更容易理解该类提供的功能,同时也让JavaBeans-aware工具能使用Java的内省功能了解该类提供的属性和操作,然后在开发工具中将它们以一种可见的、更吸引人的方式呈现出来。
        JavaBean规范描述了将任意一个类变成JavaBean的特性的完备集,你应该考虑阅读一个那个文档来提高你的Java编程技巧。对于大部分开发场景很重要的、必需的JavaBean特性列举如下:
            1) 类必须是public的,并且提供一个public的无参构造器,这样工具和应用就能动态地创建bean的新实例,而不必提前知道要使用的Java类名。像下面这样:
             String className = ...;
             Class beanClass = Class.forName(className);
             Object beanInstance = beanClass.newInstance();

            2) 作为拥有一个无参构造器的必然结果,你的bean的行为的配置必须在bean的实例化之外单独完成。这通常是通过定义一组bean的属性来完成,这些属性能用来修改bean的行为或者bean表示的数据。属性名字的一般惯例是:属性名以一个小写字母打头,剩余部分全部由合法的Java标识符组成。
            3) 通常,每个bean属性会有一个public的getter和setter方法,分别用来检索或者定义属性值。 JavaBean规范为这些名字定义了一个设计范式:属性名的首字母大写,同时加上get或者set前缀。这样,表示一个employee的JavaBean可能会有名为firstName、lastName、hireDate的属性以及像下面这样的方法签名:

             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();
             }
     
            4) 从上面的例子可以看到, boolean属性有一个特殊的变化——命名getter方法的时候使用"is"前缀而不是"get"前缀。
            5) 如果一个属性有对应的getter和setter方法,那么getter方法的返回值的数据类型必须和setter方法接受的数据类型匹配。另外,多个setter有相同的名称但是属性类型不同是违反JavaBean规范的。比如,一个bean中同时有setA(int a)、setA(long a)是违反JavaBean规范的。
            6) 为每个属性都提供一个getter方法和一个setter方法并不是必要的。在上面的例子中, fullName属性是只读的,因为没有setter方法。只写属性也是可能的,但是很不常见。
            7) 也有可能会有这样一种JavaBean:bean的getter和setter方法并不匹配上面所说的命名范式。标准的JavaBean支持Java语言中的类以及BeanUtils包中的类,允许你在与bean类有关的BeanInfo类中描述实际的属性方法名。
            8)  The JavaBeans Specification also describes many additional design patterns for event listeners, wiring JavaBeans together into component hierarchies, and other useful features that are beyond the scope of the BeanUtils package.

        使用标准的Java编码技术,如果你提前知道了你将要使用的bean类以及你感兴趣的属性,你可以很简单地处理JavaBean:
         Employee employee = ...;
         System.out.println("Hello " + employee.getFirstName() + "!");
 



    1.2 外部依赖
        commons-beanutils包在运行时需要下面这些包在应用的class path中:
    Logging Package (Apache Commons), version 1.0 or later
    Collections Package (Apache Commons), version 1.0 or later




2. Standard JavaBeans
    2.1 Background
        如上面描述的,Java编程语言的标准工具使它简单、自然,它就是:调用合适的getter方法来访问你的bean的属性值。但是在更复杂的环境中,你不一定能提前知道你将要使用的bean类或者你想要检索或者变更的属性,会怎么样?Java语言提供了像java.beans.Introspector这样的类,能在运行时检查一个Java类并确定属性的getter和setter方法的名字,再使用反射来动态调用这样一个方法。然而,这些API很难使用,并且暴露给应用开发者很多不必要的Java类的底层结构的细节。BeanUtils包中的API想要简化动态地获取和设置bean的属性,特别是应用中你要访问的对象和你关心的属性名字是在运行时确定的,而不是在编写和编译应用类的时候。
        由PropertyUtils类的静态方法所解决的一系列需求,会在该部分更进一步描述。首先,一些更深一层的定义会很有用:
         JavaBean支持的、可能的属性类型的一般集合可以分为三类——一些是标准的JavaBean规范支持的,一些是BeanUtils包唯一支持的:
            1) 简单属性——这样的属性有可以被检索或者变更的单一值。底层的属性类型可能是Java语言基本类型(例如int)、一个简单对象(例如java.lang.String)或者是一个更复杂的对象(该对象的Class由Java语言或者应用或者应用中包含的类库定义)。
            2) 索引属性——一个索引属性存储了一个 对象(这些对象有同样的类型)的有序集合,这些对象可以通过一个非负整数索引(或子脚本)来访问。或者,值的整个集合可以用一个数组来设置或者检索。作为JavaBean规范的扩展,BeanUtils  包将底层数据类型是java.util.List(或者List的实现)的属性也看成是有索引的。
            3) 映射属性——作为标准JavaBean API的扩展,BeanUtils包将底层值是java.util.Map的属性都看成是"映射的"。你可以通过一个字符串类型的key来设置和检索值。

         PropertyUtils类提供了一些API方法来获取和设置所有这些类型的属性值。在下面的代码片段,假定有两个定义有下面这些方法签名的bean类:
     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);
     }
 


    2.2 Basic Property Access
        获取和设置简单的属性值很简单。看一下Javadoc中下面的API签名:
    PropertyUtils.getSimpleProperty(Object, String)
    PropertyUtils.setSimpleProperty(Object, String, Object)

        使用这些方法,你可以在应用中动态操作 employee的name:
     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);
 

        对于索引属性,有两种选择——或者使用方括号把子脚本添加到"属性名"字符串,或者在一个单独的参数中指定子脚本给方法调用:
    PropertyUtils.getIndexedProperty(Object, String)
    PropertyUtils.getIndexedProperty(Object, String, int)
    PropertyUtils.setIndexedProperty(Object, String, Object)
    PropertyUtils.setIndexedProperty(Object, String, int, Object)

package beanutiltest;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Employee {
    private List<String> friendNames;
    private Map<String, String> attrs = new HashMap<String, String>();

    public List<String> getFriendNames() {
        return friendNames;
    }
    public void setFriendNames(List<String> friendNames) {
        this.friendNames = friendNames;
    }
    public Map<String, String> getAttrs() {
        return attrs;
    }
    public void setAttrs(Map<String, String> attrs) {
        this.attrs = attrs;
    }
}

        当添加子脚本到属性名上时,只允许整型常量。如果你需要计算你想检索的实体的索引,你可以用字符串连接组装属性名表达式。例如,你可能会做下面两种的其中一种:
        Employee e = new Employee();
        List<String> friendNames = new ArrayList<String>();
        friendNames.add("zhangsan");
        friendNames.add("lisi");
        friendNames.add("wangwu");
        friendNames.add("zhaoliu");
        e.setFriendNames(friendNames);

        // 方式一
        String name1 = (String) PropertyUtils.getIndexedProperty(e, "friendNames[0]");
        System.out.println("name1 = " + name1);

        // 方式二
        String name2 = (String) PropertyUtils.getIndexedProperty(e, "friendNames", 1);
        System.out.println("name2 = " + name2);


        以类似方式,有两种可能的方法签名用于获取和设置映射属性。区别是另外的参数由小括号而不是方括号包括,这个参数被看作String类型的key,用来从Map类型的属性中获取或设置合适的值。
    PropertyUtils.getMappedProperty(Object, String)
    PropertyUtils.getMappedProperty(Object, String, String)
    PropertyUtils.setMappedProperty(Object, String, Object)
    PropertyUtils.setMappedProperty(Object, String, String, Object)


        你可以以这两种方式的任意一种设置employee的home address:
        Employee e = new Employee();
        PropertyUtils.setMappedProperty(e, "attrs(hobby)", "cartoon");

        PropertyUtils.setMappedProperty(e, "attrs", "sport", "table tennis");

        String hobby = (String) PropertyUtils.getMappedProperty(e, "attrs", "hobby");
        System.out.println("hobby = " + hobby);

        String sport = (String) PropertyUtils.getMappedProperty(e, "attrs(sport)");
        System.out.println("sport = " + sport);
 


    2.3 Nested Property Access
        在上面所有的例子中,我们已经假定你想要检索bean(被传递给 PropertyUtils方法的第一个参数)的一个属性的值。然而,如果你检索的属性值实际上是一个Java对象,而你想要检索那个对象的一个属性,该怎么做呢?
        例如,假设我们真正想要 employee的home address的city属性。使用标准的Java编程方法直接访问bean属性,我们可能会这么写:
     String city = employee.getAddress("home").getCity();
 
         PropertyUtils使用同样的机制访问内嵌属性。要使用这个方法,你可以使用"."分隔符,将访问路径的属性名连接起来——很像你在JavaScript中执行内嵌属性访问那样。
    PropertyUtils.getNestedProperty(Object, String)
    PropertyUtils.setNestedProperty(Object, String, Object)

        和上面Java表达式等价的PropertyUtils是:
     String city = (String)   PropertyUtils.getNestedProperty(employee, "address(home).city");
 

        最终,为了方便起见, PropertyUtils提供了几个方法签名,这几个方法能接受简单属性、索引属性、映射属性访问的任意组合,可以使用任意层级的内嵌。你可以像下面那样使用:
    PropertyUtils.getProperty(Object, String)
    PropertyUtils.setProperty(Object, String, Object)

     Employee employee = ...;
     String city = (String) PropertyUtils.getProperty(employee,   "subordinate[3].address(home).city");
 




    2.4 Customizing Introspection
        像之前指出的那样, BeanUtils依赖由JavaBean规范定义的惯例来决定指定bean类的可用的属性。这样所有遵循这些规范的类都可以立即使用。
        有时一个应用不得不处理使用不同规范的类。例如,允许方法链的流畅的API并不符合JavaBean规范,因为set方法有非空返回值。从1.9.0开始, BeanUtils支持内省机制的定制。这允许一个应用扩展或者修改bean属性的默认发现。
        这个扩展机制的核心是 BeanIntrospector接口。实现了这个接口的一个对象,其目的是处理一个指定的目标类并为它检测的属性创建对应的PropertyDescriptor对象。默认地,BeanUtils使用一个DefaultBeanIntrospector对象来检测兼容JavaBean规范的属性。
        为了扩展属性发现机制,PropertyUtils提供了PropertyUtils.addBeanIntrospector(BeanIntrospector)方法,可以传入一个自定义的BeanIntrospector实现。在对一个类进行内省的时候,这个自定义的内省会被调用,然后它能将检测到的属性添加到总结果上。作为这样一个自定义BeanIntrospector实现的例子,BeanUtils推出了一个FluentPropertyBeanIntrospector类。这个实现可以检测那些set方法的返回值不是void的属性,这样就能确保在一个流畅API中支持典型属性。



    2.5 Suppressing Properties
        前一部分描述的定制bean内省机制也能用来抑制特定的属性。有一个专门的  BeanIntrospector 实现来做这件事:SuppressPropertiesBeanIntrospector。当创建一个实例的时候,应该提供一个集合,集合的内容是 bean 上不应该被访问的属性的名字。如果在处理一个 bean 类的时候,这些属性已经被其他 BeanIntrospector 实例检测到了,那么它们会被删除。
        抑制属性的一个好的使用实例是特定的类属性,这个属性默认情况下可被所有 bean 使用;这个属性根据  getClass() 方法(继承自 Object)生成,遵循属性 get 方法的命名规范。以不受控制的方式暴露这个属性会导致安全漏洞,因为它允许访问类加载器(class loader)。更多信息可以查看:https://issues.apache.org/jira/browse/BEANUTILS-463。
        因为在许多使用实例中,类属性是不希望得到的,所以已经有一个 SuppressPropertiesBeanIntrospector 实例用来抑制这个属性。它可以通过 SuppressPropertiesBeanIntrospector 的 SUPPRESS_CLASS 常量来获得。



3. Dynamic Beans (DynaBeans)
    3.1 Background
        在前面部分描述的 PropertyUtils 类被设计用作提供对已经存在的 JavaBean 类的动态属性访问,而不会以任何方式改变 JavaBean 类。用于动态属性访问的一个不同的用例是把一个动态计算出来的属性值集合表示为一个 JavaBean,但是不需要实际写一个 Java 类来表示这些属性。除了不需要创建和维护一个单独的 Java 类之外,这种能力也意味着你可以处理这么一些情况:你关心的属性的集合是动态确定的(想想把一个 select 语句的结果集表达为一组 JavaBean)。
        要支持这种用例,BeanUtils 包提供了 DynaBean 接口。DynaBean 接口必须被一个 bean 类实现,这个 bean 类必须实现该接口的方法以及关联的 DynaClass 接口。DynaClass 接口定义了被一组特别的 DynaBean 支持的属性集,就和 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);
 
        
        应该注意到一个非常重要的便利功能: PropertyUtils 中属性的 getter 和 setter 方法知道如何访问 DynaBean 中的属性。因此,如果你传递给 PropertyUtils.getSimpleProperty() 的第一个参数 bean 是一个 DynaBean 实现, 这个调用会被透明地转换成适当的  DynaBean getter方法。这样,你可以完全使用 PropertyUtils 的 API 来访问你的应用的动态属性,并且使用 PropertyUtils 的 API 来访问标准的 JavaBean 或者 DynaBean,而不用提前关心一个特定的 bean 是如何实现的。
        因为 DynaBean 和 DynaClass 是接口,它们可能会以不同的方式被实现很多次,来满足不同的使用场景。下面的小部分描述了这些实现, 作为标准  BeanUtils 包的一部分。但是,还是鼓励用户在标准实现不太有效的情况下提供自己的定制实现。


    3.2 BasicDynaBean and BasicDynaClass
       在你想要动态定义属性集(DynaProperty 实例所描述的)的情况下, BasicDynaBean 和    BasicDynaClass 实现提供了一系列基本的动态属性功能。从定义 DynaClass 开始,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 的构造器中)可以被赋值为 null。在这个例子中,dynaClass.getDynaBeanClass 的值正好是 BasicDynaBean 类。
        接下来,使用这个 DynaClass 的 newInstance() 方法创建符合 DynaClass 类的新 DynaBean 实例,并填充它的初始属性值(和你实例化一个新的标准 JavaBean 然后调用它的属性 setter 方法非常像)。

     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。通常来说,如果你使用 DynaBean,你不会关心正在使用的实际的实现类——你只关心声明它是一个 DynaBean,这样你可以使用 DynaBean API。

        如上所述,你可以将  DynaBean 实例作为第一个参数传递给 PropertyUtils 中用来获取和设置属性的方法,它会按照你所期望的方式解释执行——DynaBean 的动态属性会被检索或者修改,而不是实际的 BasicDynaBean 实现类的底层属性。

        DynaProperty[] props = new DynaProperty[] {
                new DynaProperty("name", String.class),
                new DynaProperty("age", Integer.class),
                new DynaProperty("attrs", Map.class),
                new DynaProperty("employeenames", List.class, String.class)
        };
        BasicDynaClass dynaClass = new BasicDynaClass("Boss", null, props);

        DynaBean boss = dynaClass.newInstance();
        boss.set("name", "Jimmy");
        boss.set("age", 33);
        Map<String, String> attrs = new HashMap<String, String>();
        attrs.put("hobby", "cartoon");
        attrs.put("sport", "table tennis");
        boss.set("attrs", attrs);
        List<String> employeenames = new ArrayList<String>();
        employeenames.add("zhangsan");
        employeenames.add("lisi");
        boss.set("employeenames", employeenames);


        String hobby = (String) PropertyUtils.getMappedProperty(boss, "attrs(hobby)");
        System.out.println("hobby = " + hobby);

        String employeename0 = (String) PropertyUtils.getIndexedProperty(boss, "employeenames[0]");
        System.out.println("employeename0 = " + employeename0);



    3.3 ResultSetDynaClass (Wraps ResultSet in DynaBeans)
         DynaBean API一个比较常用的使用实例是封装其他"stuff"的集合,这些"stuff"通常没有将自己呈现为 JavaBean。最常用的、适合封装的集合之一是 java.sql.ResultSet。Commons BeanUtils提供了一种标准的机制,让结果集的每一行都看作一个 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  是把一个 SQL 查询的结果表示为一组 DynaBean 的一种非常有用的技术,但是有一个重要的问题是:你的应用在处理行的过程中,底层的 ResultSet 必须保持开启。这阻碍了使用 ResultSetDynaClass 作为一种从模型层到视图层传输信息的工具的能力(在模型-视图-控制器架构,例如Struts框架),因为没有简单的机制来保证结果集最终被关闭(及底层的 Connection  返还给连接池)。
        RowSetDynaClass 针对这个问题阐述了一种不同的方法。当你构造这样一个实例,底层的数据被复制到一组代表该结果的内存 DynaBean。这个技术的优点是你可以立即关闭 ResultSet  以及对应的 Statement,通常是在你处理返回的实际数据之前。缺点是,你必须承受拷贝结果数据时的性能和内存消耗,而且结果数据必须能完全装进可用的堆内存。对许多环境(特别是web应用),这个折衷通常是很有益的。
        作为额外的好处,RowSetDynaClass 类实现了 java.io.Serializable,这样 RowSetDynaClass(以及对应结果的每一行的 DynaBean)可以被很方便地序列化和反序列化(只要底层的列的值也实现了序列化接口)。这样,RowSetDynaClass 表示了一种非常方便的方式来传输一个 SQL 查询的结果到一个远程的、基于Java的客户端应用(比如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
 


    3.5 WrapDynaBean and WrapDynaClass
        OK,现在你已经尝试了DynaBeans API,它们非常酷——很简单的 get() 和 set() 方法提供了对于 DynaBean 中所有动态定义的简单属性、索引属性和映射属性的简单访问。你用 DynaBean API 来访问你所有的 bean,但是你也有一堆已经存在的标准 JavaBean 类要处理。这时 WrapDynaBean(和关联的WrapDynaClass)开始起作用了。就像名字表述的那样,WrapDynaBean 用来把 DynaBean API 包裹到已经存在的标准 JavaBean 类。要使用它,简单地像下面这样创建 wrapper:

     MyBean bean = ...;
     DynaBean wrapper = new WrapDynaBean(bean);
     String firstName = wrapper.get("firstName");
 
        注意,尽管内部会创建适当的  WrapDynaClass,但你永远不需要处理它们。



    3.6 Lazy DynaBeans
        3.6.1. LazyDynaBean - A Lazy DynaBean
        3.6.2. LazyDynaMap - A light weight DynaBean facade to a Map with lazy map/list processing
        3.6.3. LazyDynaList - A lazy list for DynaBean's, java.util.Map's or POJO beans.
        3.6.4. LazyDynaClass - A MutableDynaClass implementation.

        你引入  DynaBean,因为它节省编写所有那些 POJO JavaBean 的代码,但是你现在在这儿,因为"lazy"引起了你的注意并让你想着"lazy"是关于什么的。让 DynaBean 的这些特点看起来"lazy"是因为下面的特性:
            1) Lazy property addition - lazy bean使用实现了 MutableDynaClass 的 DynaClass。这使得能添加和删除一个DynaClass 的属性。lazy bean 使用这个特性,在调用 set(name, value) 方法的时候自动添加还不存在的属性到DynaClass。
            2) Lazy List/Array growth - 如果一个索引属性不够大,不足以容纳设置的索引大小,那么 List 或者 Array 会自动扩容到对应的大小。
            3) Lazy List/Array instantiation - 如果一个索引属性不存在,那么调用 DynaBean 的索引属性的 getter/setter 方法 (i.e. get(name, index) or set(name, index, value))时会实例化一个新的 List 或 Array。如果索引属性还未在DynaClass 中定义,那么它会被自动添加并且会被实例化为一个默认的 List 实现。
            4) Lazy Map instantiation - 如果一个映射属性不存在,那么调用 DynaBean 的映射属性的 getter/setter 方法 (i.e. get(name, key) or set(name, key, value))时会实例化一个新的 Map。如果映射属性还未在 DynaClass 中定义,那么它会被自动添加并且会被实例化为一个默认的 Map 实现。
            5) Lazy Bean instantiation - 如果一个属性在 DynaClass 中被定义为一个 DynaBean 或者普通的 bean 并且在DynaBean 中不存在,那么 LazyDynaBean 会试图使用一个默认的空构造器实例化这个 bean。

    

        3.6.1. LazyDynaBean 是标准的 lazy bean 实现。默认情况下它关联一个实现了 MutableDynaClass 接口的LazyDynaClass--然而它可以使用任何 MutableDynaClass 实现。问题是我如何使用它?很简单,就像创建一个新的bean 然后调用 getters/setters:

     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

 

        3.6.2. LazyDynaMap 是一个用于 Map 的轻量级 DynaBean 门面,拥有所有常用的 lazy 特性。它很轻量,因为没有一个关联的、包含所有属性的 DynaClass。实际上它自己实现了 DynaClass(和MutableDynaClass),并且从 Map 的实际内容派生出所有 DynaClass 信息。LazyDynaMap 可以基于一个已经存在的 Map 创建,也可以实例化自己的 Map。在所有 DynaBean 处理完成后,Map 会被收回,DynaBean 门面会被丢弃。
    如果你需要一个新的 Map,那么使用:

     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:

     Map myMap = ....                             // exisitng Map
     DynaBean dynaBean = new LazyDynaMap(myMap);  // wrap Map in DynaBean
     dynaBean.set("foo", "bar");                  // set properties
 

        3.6.3. LazyDynaList 是用于DynaBean java.util.Map或者POJO bean的DynaBeans。查看Javadoc获取更多细节和示例使用。


        3.6.4. LazyDynaClass 继承自 BasicDynaClass,实现了 MutableDynaClass 接口。它可以和其他 DynaBean 实现一起使用,但是它是 LazyDynaBean 使用的默认 DynaClass。当使用 LazyDynaBean 的时候,没有必要去管 DynaClass 。然而有时候需要首先设置 DynaClass——也许是为一个索引属性定义数组的类型,或者是在严格模式使用 DynaBean 时需要。这样做更直截了当,或者先创建一个 LazyDynaClass:

     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
 
        或者创建一个LazyDynaBean然后获取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 的一个特性是:它有一个受限属性。当 DynaClass 受限的时候,不能从 DynaClass 中添加或者删除属性。如果 DynaClass 受限,LazyDynaBean 和 LazyDynaMap 都不能自动添加属性。




4. Data Type Conversions
    4.1 Background
        到目前为止,我们只考虑了这些情况:动态访问的属性的数据类型是已知的,或者我们可以使用 Java 转换来执行类型转换。那么当不能进行 cast,而你想要自动执行类型转换时要怎么办?BeanUtils 包也提供了许多 API 和设计范式用来执行这个任务。


    4.2 BeanUtils and ConvertUtils Conversions
        一种非常常见的使用实例(以及导致 BeanUtils 包的初始创建的情景)是将一个 web 应用接收到的javax.servlet.HttpServletRequest 中的请求参数转换为在一个任意的 JavaBean 上的一组对应的属性 setter 调用。 这是 Struts 框架提供的基础服务之一,它内部使用 BeanUtils 来实现这个功能。

        在一个 HTTP 请求中,包含的参数以一系列 String 实例(或者是 String 数组,如果同一个参数名有多个值时)的方式组合,这些参数需要转换成基础的数据类型。BeanUtils 类提供了属性 setter 方法,可以接受 String 值并自动将它们转换成适当的属性类型,如 int 或 boolean,并且属性 getter 方法可以执行反向转换。最后,populate() 方法接受一个java.util.Map,该 Map 包含了一组属性值(以属性名为 key),然后当底层的 bean 有一个同名的属性时会调用适当的setter 方法。所以,你可以像这样执行一体化的属性设置操作: 

     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 类中定义的转换方法来执行实际的转换,这些方法也可以直接使用。
        WARNING - 很可能 ConvertUtils 中方法的硬编码使用会在将来被废弃,作为代替,会替换为一种机制,允许你插入自己的 Converter 接口实现。因此,新的代码不应该依赖 ConvertUtils 来写。



    4.3 Defining Your Own Converters
        ConvertUtils 类支持定义和注册你自己的对于任意给定的 Java 类的 String --> Object 转换。 一旦注册,这样的转换就会透明地被所有 BeanUtils 方法调用(包括 populate())。要创建和注册自己的转换器,按照下面这些步骤:
            写一个类,实现 Converter 接口。convert() 方法应该接受你的应用类的 java.lang.Class 对象(例如,你想要转换成的类)以及一个代表传进来的要被转换的 String。
            在应用启动时候,调用 ConvertUtils.register() 方法注册一个你的转换类的实例。



    4.4 Locale Aware Conversions
        org.apache.commons.beanutils 中的标准类并不是 locale aware 的。这给予它们一个更干净的接口并让它们在locale 不重要的场景更容易使用。
        扩展开来,locale-aware 类似物可以在 org.apache.commons.beanutils.locale 中找到。




5. Utility Objects And Static Utility Classes
    5.1 Background
        到目前为止,已经讲述了静态工具类——BeanUtils, ConvertUtils 和 PropertyUtils。这些类易于使用但是不太灵活。它们共享同样的注册转换器和同样的 cache。
        通过工具对象也可以访问这个功能。事实上,静态工具类使用这些类的 worker 实例。对于每一个静态工具类,有一个相应的类具有同样的功能,并且可以被实例化:

Static Utility Class                             Utility Object
BeanUtils                                           BeanUtilsBean
ConvertUtils                                       ConvertUtilsBean
PropertyUtils                                      PropertyUtilsBean




6. Collections
    6.1 Comparing Beans
        org.apache.commons.beanutils.BeanComparator是一个 Comparator 实现,用于比较基于一个共享的属性值的bean。

    6.2 Operating On Collections Of Beans
        commons-collections 中的 Closure 接口封装了一块代码,用于在一个任意输入的 Object上执行。Commons-collections 包含了允许将 Closure 应用到一个 Collection 的内容上的代码。更多细节,可以查看 commons-collections 文档。
        BeanPropertyValueChangeClosure 是一个 Closure,用于给一个指定的属性设置一个特定的值。一个典型的使用是将之和 commons-collections 综合起来使用,这样一个集合中的所有 bean 都可以将一个指定的属性设置一个特定的值。  
例如,将整个集合中的 activeEmployee 属性设置为 TRUE:

     // create the closure
     BeanPropertyValueChangeClosure closure =
         new BeanPropertyValueChangeClosure( "activeEmployee", Boolean.TRUE );

     // update the Collection
     CollectionUtils.forAllDo( peopleCollection, closure );
   

    6.3 Querying Or Filtering Collections Of Beans
        commons-collections 中的 Predicate 接口封装了一个对输入Object的计算,该计算返回 true 或 false。 Commons-collections 包含了允许将 Predicate 应用于过滤集合的代码。更多细节,可以查看commons-collections文档。
        BeanPropertyValueEqualsPredicate是一个 Predicate。一个典型的使用是将之与commons-collections综合起来,基于一个属性值来过滤集合。
        例如,要过滤一个集合,找出所有activeEmployee是false的bean:

     BeanPropertyValueEqualsPredicate predicate =
         new BeanPropertyValueEqualsPredicate( "activeEmployee", Boolean.FALSE );

     // filter the Collection
     CollectionUtils.filter( peopleCollection, predicate );
 


    6.4 Transforming Collections Of Beans
        commons-collections中的 Transformer 接口封装了将一个输入的 Object 转换为一个输出对象的转换。Commons-collections 包含允许将 Transformer 用于从一个输入集合中产生一个输出集合的代码。更多细节,可以查看commons-collections 文档。
        BeanToPropertyTransformer 是一个 Transformer 实现,用于将一个bean转换成它的属性值。
        例如,要找出每个bean的person属性的address中的所有城市: 

     // create the transformer
     BeanToPropertyValueTransformer transformer = new BeanToPropertyValueTransformer( "person.address.city" );

     // transform the Collection
     Collection peoplesCities = CollectionUtils.collect( peopleCollection, transformer );
     



7. Frequently Asked Questions
    7.1 Why Can't BeanUtils Find My Method?
        BeanUtils 包依赖于内省而不是反射。这意味着它只会找出兼容 JavaBean 的属性。
        一个属性只能有一个 set 和 get 方法。不允许覆盖。
        The java.beans.Introspector searches widely for a custom BeanInfo class. If your class has the same name as another with a custom BeanInfo (typically a java API class) then the Introspector may use that instead of creating via reflection based on your class. If this happens, the only solution is to create your own BeanInfo.



    7.2 How Do I Set The BeanComparator Order To Be Ascending/Descending?
        BeanComparator 依赖于一个内部的 Comparator 来执行实际的比较。默认情况下,使用org.apache.commons.collections.comparators.ComparableComparator 加上一个自然顺序。如果你想要改变顺序,那么应该创建一个定制的 Comparator 并传递给适当的构造器。
例如:

     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);
     ...
 





猜你喜欢

转载自blog.csdn.net/frankingly/article/details/50858321