欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

【译文】BeanUtils的介绍
(last modified Jun 26, 2023, 2:04 PM )
by
侧边栏壁纸
  • 累计撰写 178 篇文章
  • 累计创建 62 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

【译文】BeanUtils的介绍

橙序员
2022-08-07 / 0 评论 / 0 点赞 / 447 阅读 / 7,641 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

1. 概览1.1背景JavaBeans 名称来自 Java API,用于 Java 语言的组件架构。编写符合 JavaBeans 设计模式的 Java 类使 Java 开发人员更容易理解您的类提供的功能,并允许 JavaBeans 感知工具使用 Java 的自省功能来了解您的类提供的属性和操作,并在

1. 概览

1.1背景

JavaBeans 名称来自 Java API,用于 Java 语言的组件架构。编写符合 JavaBeans 设计模式的 Java 类使 Java 开发人员更容易理解您的类提供的功能,并允许 JavaBeans 感知工具使用 Java 的自省功能来了解您的类提供的属性和操作,并在开发工具中以视觉上吸引人的方式呈现它们。

JavaBeans 规范描述了使任意Java 类成为JavaBean 或不成为JavaBean 的完整特征集——您应该考虑阅读本文档作为培养Java 编程技能的重要部分。但是,这里列出了对大多数开发场景很重要的 JavaBean 所需特性:

  • 该类必须是公共的,并提供一个不接受任何参数的公共构造函数。这允许工具和应用程序动态地创建 bean 的新实例,而不必提前知道将使用什么 Java 类名,如下所示:

             String className = ...;
             Class beanClass = Class.forName(className);
             Object beanInstance = beanClass.newInstance();
    
  • 作为无参数构造函数的必然结果,bean 行为的配置必须与其实例化分开完成。 这通常通过定义 bean 的一组属性来完成,这些属性可用于修改其行为或 bean 表示的数据。 属性名称的常规约定是它们以小写字母开头,并且仅由 Java 标识符中合法的字符组成。

  • 通常,每个 bean 属性都有一个公共的 getter 和 setter 方法,分别用于检索或定义属性的值。 JavaBeans 规范为这些名称定义了一种设计模式,使用 get 或 set 作为属性名称的前缀,其首字母大写。 因此,代表员工的 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();
             }
    
  • 正如您从上面的示例中看到的,布尔属性有一个特殊的变体——您可以使用 is 前缀而不是 get 前缀命名 getter 方法,如果这样可以使方法名称更易于理解。

  • 如果属性同时具有 getter 和 setter 方法,则 getter 返回的数据类型必须与 setter 接受的数据类型相匹配。 此外,具有多个同名但属性类型不同的 setter 是违反 JavaBeans 规范的。

  • 不需要为每个属性都提供 getter 和 setter。 在上面的示例中,fullName 属性是只读的,因为没有 setter 方法。 也可以提供只写属性,但不太常见。

  • 也可以创建一个 JavaBean,其中 getter 和 setter 方法与上述命名模式不匹配。 标准 JavaBeans 支持 Java 语言中的类,以及 BeanUtils 包中的所有类,允许您在与您的 bean 类关联的 BeanInfo 类中描述实际的属性方法名称。 有关完整的详细信息,请参阅 JavaBeans 规范。

  • JavaBeans 规范还描述了事件侦听器的许多其他设计模式,将 JavaBeans 连接到组件层次结构中,以及超出 BeanUtils 包范围的其他有用功能。

使用标准 Java 编码技术,如果您提前知道您将使用哪些 bean 类以及您对哪些属性感兴趣,那么处理 JavaBean 将非常容易:

         Employee employee = ...;
         System.out.println("Hello " + employee.getFirstName() + "!");

1.2 额外依赖

commons-beanutils 包要求在运行时在应用程序的类路径中提供以下附加包:

日志记录包 (Apache Commons),版本 1.0 或更高版本
集合包 (Apache Commons),1.0 版或更高版本

2. 标准java bean

2.1 背景

如上所述,Java 编程语言的标准工具使得通过调用适当的 getter 方法来访问 bean 的属性值变得容易和自然。但是在更复杂的环境中会发生什么,您不一定提前知道您将使用哪个 bean 类,或者您想要检索或修改哪个属性? Java 语言提供了像 java.beans.Introspector 这样的类,它可以在运行时检查 Java 类并为您识别属性 getter 和 setter 方法的名称,以及动态调用此类方法的反射功能。但是,这些 API 可能难以使用,并且会使应用程序开发人员接触到 Java 类底层结构的许多不必要的细节。 BeanUtils 包中的 API 旨在简化动态获取和设置 bean 属性,其中您正在访问的对象——以及您关心的属性的名称——在应用程序的运行时确定,而不是在您编写时确定并编译您的应用程序的类。

这是由 PropertyUtils 类的静态方法满足的一组需求,本节将对此进行进一步描述。然而,首先,一些进一步的定义将被证明是有用的:

JavaBean 支持的可能属性类型的一般集合可以分为三类——其中一些由标准 JavaBeans 规范支持,而另一些由 BeanUtils 包唯一支持:

  • 简单 - 简单或标量属性具有可以检索或修改的单个值。 底层属性类型可能是 Java 语言原语(例如 int、简单对象(例如 java.lang.String)或更复杂的对象,其类由 Java 语言、应用程序或 应用程序中包含的类库。
  • 索引 - 索引属性存储可以由整数值非负索引(或下标)单独访问的对象的有序集合(所有相同类型)。 或者,可以使用数组设置或检索整组值。 作为对 JavaBeans 规范的扩展,BeanUtils 包将任何其基础数据类型为 java.util.List(或 List 的实现)的属性都考虑为索引。
  • 映射 - 作为标准 JavaBeans API 的扩展,BeanUtils 包将其基础值为 java.util.Map 的任何属性视为“映射”。 您可以通过字符串值键设置和检索单个值。

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 基本属性访问

获取和设置简单的属性值非常简单:-)。 查看 Javadocs 中的以下 API 签名:

PropertyUtils.getSimpleProperty(Object, String)
PropertyUtils.setSimpleProperty(Object, String, Object)

使用这些方法,您可以在应用程序中动态操作员工的姓名:

     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)

为属性名称添加下标时,只允许使用整数常量。 如果需要计算要检索的条目的索引,可以使用字符串连接来组装属性名称表达式。 例如,您可以执行以下任一操作:

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

以类似的方式,有两种可能的方法签名用于获取和设置映射属性。 不同之处在于额外的参数被括号(“(”和“)”)而不是方括号包围,并且它被认为是用于从底层映射获取或设置适当值的字符串值键。

PropertyUtils.getMappedProperty(Object, String)
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 嵌套属性访问

在上述所有示例中,我们假设您希望检索作为第一个参数传递给 PropertyUtils 方法的 bean 的属性值。 但是,如果您检索的属性值确实是一个 Java 对象,而您希望检索该对象的属性,该怎么办?

例如,假设我们真的想要员工家庭住址的 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 自定义内省

正如所指出的,BeanUtils 依赖于 JavaBeans 规范定义的约定来确定可用于特定 bean 类的属性。因此,所有符合这些约定的类都可以开箱即用。

有时应用程序必须使用不同的约定来处理类。例如,允许方法链接的流式 API 不符合 JavaBeans 规范,因为这里设置的方法具有非 void 返回值。从 1.9.0 版本开始,BeanUtils 支持自定义其自省机制。这允许应用程序扩展或修改 bean 属性的默认发现。

这种扩展机制的关键是 BeanIntrospector 接口。实现此接口的对象的目的是处理特定的目标类并为其检测到的属性创建相应的 PropertyDescriptor 对象。默认情况下,BeanUtils 使用 DefaultBeanIntrospector 对象,该对象检测与 JavaBeans 规范兼容的属性。

为了扩展属性发现机制,PropertyUtils 提供了 PropertyUtils.addBeanIntrospector(BeanIntrospector) 方法。这里可以传入一个自定义的 BeanIntrospector 实现。在一个类的自省期间,然后调用这个自定义的自省器,它可以将它检测到的属性添加到总结果中。作为此类自定义 BeanIntrospector 实现的示例,BeanUtils 附带了 FluentPropertyBeanIntrospector 类。此实现可以检测其 set 方法具有非 void 返回类型的属性 - 从而支持流式 API 中的典型属性。

2.5 抑制属性

上一节中描述的自定义 bean 自省机制也可以用来抑制特定的属性。有一个专门的 BeanIntrospector 实现可以做到这一点:SuppressPropertiesBeanIntrospector。创建实例时,必须提供一个集合,该集合具有不应在 bean 上访问的属性名称。如果在处理 bean 类期间其他 BeanIntrospector 实例检测到这些属性,则这些属性将被删除。

抑制属性的一个很好的用例是特殊类属性,默认情况下它可用于所有 bean;它是从继承自 Object 的 getClass() 方法生成的,该方法遵循属性获取方法的命名约定。以不受控制的方式公开此属性可能会导致安全漏洞,因为它允许访问类加载器。更多信息可以在 https://issues.apache.org/jira/browse/BEANUTILS-463 找到。

因为在许多用例中类属性是不受欢迎的,所以已经有一个 SuppressPropertiesBeanIntrospector 的实例被配置为抑制这个属性。它可以通过 SuppressPropertiesBeanIntrospector 的 SUPPRESS_CLASS 常量获得。

3. 动态bean

3.1 背景

上一节中描述的 PropertyUtils 类旨在提供对现有 JavaBean 类的动态属性访问,而无需以任何方式修改它们。动态属性访问的另一个用例是,当您希望将动态计算的一组属性值表示为 JavaBean,但不必实际编写 Java 类来表示这些属性时。除了不必创建和维护单独的 Java 类而节省工作量之外,这种能力还意味着您可以处理动态确定您关心的属性集的情况(考虑将 SQL 选择的结果集表示为一个集合JavaBeans …)。

为了支持这个用例,BeanUtils 包提供了 DynaBean 接口,该接口必须由实际实现接口方法的 bean 类实现,以及定义一组特定 DynaBeans 支持的属性集的关联 DynaClass 接口,在很大程度上与 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 方法了解如何访问 DynaBeans 中的属性。因此,如果您作为第一个参数传递给的 bean,比如 PropertyUtils.getSimpleProperty() 确实是一个 DynaBean 实现,则调用将透明地转换为适当的 DynaBean getter 方法。因此,如果您愿意,您可以将应用程序的动态属性访问完全基于 PropertyUtils API,并使用它们来访问标准 JavaBeans 或 DynaBeans,而不必提前关心特定 bean 是如何实现的。

因为DynaBean和DynaClass是接口,它们可能会以不同的方式多次实现,以应对不同的使用场景。以下小节描述了作为标准 BeanUtils 包的一部分提供的实现,但鼓励您在标准实现不够用的情况下提供自己的自定义实现。

3.2 BasicDynaBeanasicDynaClass

BasicDynaBean 和 BasicDynaClass 实现提供了一组基本的动态属性功能,您可以在其中动态定义一组属性(由 DynaProperty 的实例描述)。 您首先定义建立您关心的属性集的 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 然后调用其属性设置器一样):

     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。 一般来说,如果您正在使用 DynaBeans,您将不需要关心正在使用的实际实现类——您只关心声明它是一个 DynaBean,以便您可以使用 DynaBean API。

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

3.3 ResultSetDynaClass(在 DynaBeans 中包装 ResultSet)

DynaBean API 的一个非常常见的用例是包装其他通常不以 JavaBeans 呈现的“东西”集合。 最好包装的最常见的集合之一是 java.sql.ResultSet,当您要求 JDBC 驱动程序执行 SQL SELECT 语句时,它会返回。 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(断开连接的 ResultSet 作为 DynaBeans)

尽管 ResultSetDynaClass 是将 SQL 查询的结果表示为一系列 DynaBean 的一种非常有用的技术,但一个重要的问题是底层 ResultSet 必须在应用程序处理行的整个时间段内保持打开状态。这阻碍了使用 ResultSetDynaClass 作为在模型-视图-控制器架构(例如 Struts 框架提供的架构)中将信息从模型层传递到视图层的能力,因为没有简单的机制来确保结果集最终关闭(并且底层连接返回到它的连接池,如果你正在使用一个)。

RowSetDynaClass 类代表了解决此问题的不同方法。当您构造这样一个实例时,底层数据被复制到一组表示结果的内存中 DynaBeans。当然,这种技术的优点是您可以立即关闭 ResultSet(和相应的语句),通常甚至在您处理返回的实际数据之前。当然,缺点是您必须支付复制结果数据的性能和内存成本,并且结果数据必须完全适合可用的堆内存。对于许多环境(尤其是在 Web 应用程序中),这种权衡通常是非常有益的。

作为一个额外的好处,定义了 RowSetDynaClass 类来实现 java.io.Serializable,这样它(以及与结果的每一行对应的 DynaBeans)可以方便地序列化和反序列化(只要底层的列值也是可序列化)。因此,RowSetDynaClass 代表了一种将 SQL 查询结果传输到远程基于 Java 的客户端应用程序(例如小程序)的非常方便的方法。

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 WrapDynaBeanWrapDynaClass

好的,您已经尝试过 DynaBeans API,它们很酷 —— 非常简单的 get() 和 set() 方法可以轻松访问 DynaBeans 的所有动态定义的简单、索引和映射属性。 您想使用 DynaBean API 来访问所有 bean,但是您也有一堆现有的标准 JavaBeans 类需要处理。 这就是 WrapDynaBean(及其关联的 WrapDynaClass)发挥作用的地方。 顾名思义,WrapDynaBean 用于围绕现有的标准 JavaBean 类“包装”DynaBean API。 要使用它,只需像这样创建包装器:

     MyBean bean = ...;
     DynaBean wrapper = new WrapDynaBean(bean);
     String firstName = wrapper.get("firstName");

请注意,虽然适当的 WrapDynaClass 实例是在内部创建的,但您永远不需要处理它们。

3.6惰性DynaBeans

  1. LazyDynaBean - 一个惰性的 DynaBean
  2. LazyDynaMap - 具有惰性映射/列表处理的轻量级 DynaBean 外观
  3. LazyDynaList - DynaBean、java.util.Map 或 POJO bean 的惰性列表。
  4. LazyDynaClass - MutableDynaClass 实现。

您使用 DynaBeans 是因为它节省了所有 POJO JavaBeans 的编码,但您来到这里是因为懒加载引起了您的注意并想知道那是什么? 使这些 DynaBean 变得懒加载的原因在于以下特性:

  • 惰性属性添加 - 惰性 bean 使用实现 MutableDynaClass 接口的 DynaClass。这提供了添加和删除 DynaClass 属性的能力。当调用 set(name, value) 方法时,惰性 bean 使用此功能自动将不存在的属性添加到 DynaClass。
  • 惰性列表/数组增长 - 如果索引属性不足以容纳正在设置的索引,则列表或数组会自动增长以使其如此。
  • 惰性列表/数组实例化 - 如果索引属性不存在,则调用 DynaBean 的索引属性 getter/setter 方法(即 get(name, index) 或 set(name, index, value))会生成新的 List 或 Array被实例化。如果索引属性没有在 DynaClass 中定义,那么它会被自动添加并实例化一个默认的 List 实现。
  • Lazy Map 实例化 - 如果映射属性不存在,则调用 DynaBean 的映射属性 getter/setter 方法(即 get(name, key) 或 set(name, key, value))会导致实例化一个新 Map。如果映射的属性没有在 DynaClass 中定义,那么它会被自动添加并实例化一个默认的 Map 实现。
  • Lazy Bean 实例化 - 如果属性在 DynaClass 中定义为 DynaBean 或常规 bean,并且在 DynaBean 中不存在,则 LazyDynaBean 将尝试使用默认的空构造函数实例化 bean。
  1. LazyDynaBean 是标准的惰性 bean 实现。 默认情况下,它与实现 MutableDynaClass 接口的 LazyDynaClass 相关联 - 但是它可以与任何 MutableDynaClass 实现一起使用。 问题是我如何使用它? - 它可以像创建一个新bean然后调用getter/setter一样简单…

         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 是 Map 的轻量级 DynaBean 外观,具有所有常见的惰性特性。 它的重量很轻,因为它没有包含所有属性的关联 DynaClass。 事实上,它实际上实现了 DynaClass 接口本身(和 MutableDynaClass),并从 Map 的实际内容中派生出所有 DynaClass 信息。 LazyDynaMap 可以围绕现有地图创建,也可以实例化自己的地图。 在任何 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. LazyDynaList 是 DynaBeans java.util.Map 或 POJO bean 的惰性列表。 有关更多详细信息和示例用法,请参阅 Javadoc。

  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
     
    

4. 数据类型转换

4.1 背景

到目前为止,我们只考虑了动态访问属性的数据类型已知以及可以使用 Java 强制转换执行类型转换的情况。 如果您想在无法进行类型转换时自动执行类型转换会发生什么? BeanUtils 包也提供了各种 API 和设计模式来执行此任务。

4.2 BeanUtils 以及 ConvertUtils 转换

一个非常常见的用例(以及导致最初创建 BeanUtils 包的情况)是希望将 Web 应用程序接收到的 javax.servlet.HttpServletRequest 中包含的一组请求参数转换为一组相应的属性对任意 JavaBean 的 setter 调用。 (这是 Struts 框架提供的基础服务之一,它在内部使用 BeanUtils 来实现此功能。)

在 HTTP 请求中,包含的参数集以一系列 String(或 String 数组,如果同一个参数名称有多个值)实例的形式提供,这些实例需要转换为基础数据类型。 BeanUtils 类提供了接受字符串值的属性设置器方法,并自动将它们转换为适用于 Java 原语的属性类型(例如 int 或布尔值),以及执行反向转换的属性获取器方法。最后,提供了一个 populate() 方法,该方法接受包含一组属性值(以属性名称为键)的 java.util.Map,并在底层 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 类中定义的转换方法来执行实际的转换,这些方法也可以直接使用。 警告 - 将来可能会弃用 ConvertUtils 方法的硬编码使用,取而代之的是允许您插入自己的 Converter 接口实现的机制。 因此,不应依赖 ConvertUtils 来编写新代码。

4.3 定义自己的转换器

ConvertUtils 类支持为任何给定的 Java 类定义和注册您自己的 String --> Object 转换的能力。 一旦注册,这些转换器将被所有 BeanUtils 方法(包括 populate())透明地使用。 要创建和注册您自己的转换器,请执行以下步骤:

编写一个实现 Converter 接口的类。 convert() 方法应该接受您的应用程序类的 java.lang.Class 对象(即您要转换到的类,以及一个表示要转换的传入值的字符串。
在应用程序启动时,通过调用 ConvertUtils.register() 方法注册转换器类的实例。

org.apache.commons.beanutils 中的标准类不支持语言环境。 这为他们提供了一个更清晰的界面,并使其在语言环境不重要的情况下更易于使用。

可以在 org.apache.commons.beanutils.locale 中找到扩展的、区域感知的类似物。 这些都是按照与基本类相同的方式构建的,但支持本地化。

5. 实用程序对象和静态实用程序类

到目前为止,示例已经涵盖了静态实用程序类(BeanUtils、ConvertUtils 和 PropertyUtils)。 这些很容易使用,但有些不灵活。 这些都共享相同的注册转换器和相同的缓存。

此功能也可以通过实用程序对象访问(实际上,静态实用程序类使用这些类的工作实例)。 对于每个静态实用程序类,都有一个可以实例化的具有相同功能的对应类:

静态工具类 工具对象
BeanUtils BeanUtilsBean
ConvertUtils ConvertUtilsBean
PropertyUtils PropertyUtilsBean

创建实例允许保证对创建它的代码的缓存和注册的控制。

6. 集合

6.1 比较 Beans

org.apache.commons.beanutils.BeanComparator 是一个 Comparator 实现,它根据共享属性值比较 bean。

6.2 对 Bean 集合进行操作

commons-collections 中的 Closure 接口封装了在任意输入对象上执行的代码块。 Commons-collections 包含允许将闭包应用于集合内容的代码。 有关更多详细信息,请参阅 commons-collections 文档。

BeanPropertyValueChangeClosure 是一个将指定属性设置为特定值的闭包。 一个典型的用法是将它与 commons-collections 结合起来,这样一个集合中的所有 bean 都可以将一个特定的属性设置为一个特定的值。

例如,将整个集合的 activeEmployee 属性设置为 TRUE:

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

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

6.3 查询或过滤 Bean 集合

commons-collections 中的 Predicate 接口封装了对返回 true 或 false 的输入对象的评估。 Commons-collections 包含允许应用谓词以用于过滤集合的代码。 有关更多详细信息,请参阅 commons-collections 文档。

BeanPropertyValueEqualsPredicate 是一个 Predicate,它根据给定值评估设置的属性值。 一个典型的用法是(与 commons-collections 结合使用)根据属性值过滤集合。

例如,要过滤一个集合以查找所有活动员工为假的 bean,请使用:

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

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

6.4 转换 Bean 的集合

commons-collections 中的 Transformer 接口封装了输入对象到输出对象的转换。 Commons-collections 包含的代码允许应用 Transformer 从输入集合中生成输出集合。 有关更多详细信息,请参阅 commons-collections 文档。

BeanToPropertyTransformer 是一个将 bean 转换为它的属性值的 Transformer 实现。

例如,要查找集合中每个 bean 的每个 person 属性的地址中包含的所有城市:

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

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

评论区