Spring 4.x Bean操作和BeanWrapper

BeanWrapper的工作方式在一定程度上是由它的名字表示:它包装一个Bean在其上执行动作,例如设置和检索属性。

在beans包中的一个很重要的类是BeanWrapper接口和它响应的实现(BeanWrapperImpl)。BeanWrapper提供功能(单独或批量)设置和获取属性值,获取属性描述,和查询属性决定它们是否可读或可写。同时,BeanWrapper提供支持嵌套属性,能够设置属性的无限深度的子属性。此外,BeanWrapper支持添加标准JavaBeans的PropertyChangeListeners和VetoableChangeListener,无需在目标类中提供代码。最后但并不是最不重要的,BeanWrapper提供设置或索引属性支持。BeanWrapper通常并不直接用于应用代码,但用于DataBinder和BeanFactory。

BeanWrapper的工作方式在一定程度上是由它的名字表示:它包装一个Bean在其上执行动作,例如设置和检索属性。

1    设置和获取基础和嵌套属性

使用setPropertyValue(s)和getPropertyValue(s)方法设置和获取属性,都有两个变体。更详细的描述见Spring的Javadoc中。

表 属性的例子

表达式 描述
name 表示属性名name对应方法getName()或isName()和setName()。
account.name 表示属性account的嵌套属性name,对应方法getAccount().setName()或getAccount().getName()。
account[2] 表示索引属性account的第三个元素。索引属性的类型可以是数组、list或其他自然排序集合。
account[COMPANYNAME] 表示Map实体索引的值,键为COMPENYNAME。

下面你会发现的一些示例使用BeanWrapper获取和设置属性。

public class Company {

private String name;
    private Employee managingDirector;

public String getName() {
        return this.name;
    }

public void setName(String name) {
        this.name = name;
    }

public Employee getManagingDirector() {
        return this.managingDirector;
    }

public void setManagingDirector(Employee managingDirector) {
        this.managingDirector = managingDirector;
    }
}

public class Employee {

private String name;

private float salary;

public String getName() {

return this.name;

}

public void setName(String name) {

this.name = name;

}

public float getSalary() {

return salary;

}

public void setSalary(float salary) {

this.salary = salary;

}

}

下面代码片段显示一些例子,如何检索和操作Companies和Employees实例的一些属性:

BeanWrapper company = new BeanWrapperImpl(new Company());
// 设置公司名称
company.setPropertyValue("name", "Some Company Inc.");
// 也可以这样
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);

// 创建一个主管并绑定到公司
BeanWrapper jim = new BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());

// 通过公司检索主管的薪水
Float salary = (Float) company.getPropertyValue("managingDirector.salary");

2    内置PropertyEditor实现

Spring使用PropertyEditor的概念作用于Object和String之间的转换。如果你了解它,有时可能会方便以不同的方式表示属性而不是对象本身。例如,Date可以表示为一个人类可读的方式(作为字符串’2007-14-09’),然而,我们任然能够将人类可读回溯到原始日期(甚至更好:转换任意日期为人类可读形式,回溯至Date对象)。这种行为可以通过注册java.beans.PropertyEditor类型的自定义编辑器。在BeanWrapper上注册自定义编辑器或在一个如前一节中所述特定的IoC容器中,指定如何将属性转换为特定类型。

在Spring中编辑属性的一些例子:

  • 在Bean上使用PropertyEditors设置属性。当java.lang.String作为某些Bean的属性值声明在XML文件中,(如果设置相应属性有Class参数)Spring将使用ClassEditor尝试解析参数为一个Class对象。
  • 在Spring的MVC框架中使用所有类型的PropertyEditors解析HTTP请求参数,你能手动绑定CommandController的所有子类。

Spring有一系列的内置PropertyEditors。每个PropertyEditors都列在org.springframework.beans.propertyeditors包中。大多数,但不是所有(如下所示),默认通过BeanWrapperImpl注册。以某种方式属性编辑器是可配置的,你当然还可以注册自己的变体来覆盖默认的属性编辑器:

表 内置属性编辑器


说明
ByteArrayPropertyEditor 编辑字节数组。字符串会被转换成相应的字节表示。默认由BeanWrapperImpl注册。
ClassEditor 解析字符串代表的类为真实类,反之亦然。当一个类没有找到时,抛出IllegalArgumentException。默认由BeanWrapperImpl注册。
CustomBooleanEditor Boolean属性的自定义属性编辑器。默认由BeanWrapperImpl注册,但,能通过注册自定义实例作为自定义编辑器来覆盖。
CustomCollectionEditor 集合属性编辑器,转换任意源Collection到指定目标Collection类型。
CustomDateEditor java.util.Date的自定属性编辑器,支持自定义DateFormat。默认没有注册。必须根据用户需求注册适当的格式。
CustomNumberEditor 任意Number子类,像,Integer、Long、Float、Double的自定义属性编辑器。默认由BeanWrapperImpl注册,但,能通过注册自定义实例作为自定义编辑器来覆盖。
FileEditor 能解析字符串为java.io.File对象。默认由BeanWrapperImpl注册。
InputStreamEditor 单向属性编辑器,能够(通过中间ResourceEditor和Resource)把一个字符串转换为InputStream,因此InputStream可用直接设置为字符串。注意,默认不会为你关闭InputStream!默认由BeanWrapperImpl注册。
LocaleEditor 能够解析字符串为Locale对象,反之亦然(字符串格式是[国家][变体],Locale的toString()方法提供了相同的功能)。默认由BeanWrapperImpl注册。
PatternEditor 能够解析字符串为java.util.regex.Pattern对象,反之亦然。
PropertiesEditor 能够(使用定义在java.util.Properties类的javadocs的格式)转换字符串为Properties对象。默认由BeanWrapperImpl注册。
StringTrimmerEditor 去掉字符串两边空格的属性编辑器。允许选择将一个空字符串转换为null。默认没有注册,必须用户注册。
URLEditor 能够解析代表URL的字符串为一个真实URL对象。默认由BeanWrapperImpl注册。

Spring使用java.beans.PropertyEditorManager设置搜索需要的属性编辑器的路径。搜索路径也包括sun.bean.editors,包括PropertyEditor实现类型,例如Font、Color和大多数原始类型。也要注意,标准JavaBeans基础设施将自动发现PropertyEditor类(你不必显示注册它们)如果他它们和处理的类在同一个包中,并与处理的类相同的名称,使用Editor结尾;例如,下面有一个可能的类和包结构,FooEditor将被认为是使用Foo类型属性的PropertyEditor 。

com
  chank
    pop
      Foo
      FooEditor // Foo类的PropertyEditor

注意,你也能在这里使用标准BeanInfo JavaBeans机制。找到下面的一个例子使用BeanInfo机制明确注册一个或多个PropertyEditor实例的属性相关联的类。

com
  chank
    pop
      Foo
            FooBeanInfo // Foo类的BeanInfo

以下是引用FooBeanInfo类的Java源代码。这将关联CustomNumberEditor与Foo类的age属性。

public class FooBeanInfo extends SimpleBeanInfo {

public PropertyDescriptor[] getPropertyDescriptors() {
        try {
            final PropertyEditor numberPE = new CustomNumberEditor(Integer.class, true);
            PropertyDescriptor ageDescriptor = new PropertyDescriptor("age", Foo.class) {
                public PropertyEditor createPropertyEditor(Object bean) {
                    return numberPE;
                };
            };
            return new PropertyDescriptor[] { ageDescriptor };
        }
        catch (IntrospectionException ex) {
            throw new Error(ex.toString());
        }
    }
}

注册额外的自定义PropertyEditors

当设置Bean属性作为字符串值,Spring IoC容器最后使用标准JavaBeans PropertyEditor转换这些字符串为复杂属性类型。Spring预先注册了一系列自定义PropertyEditor(例如,为了转换类名字符串为真实Class对象)。此外,Java的标准JavaBeans PropertyEditor查找机制允许PropertyEditor的类仅仅是适当的命名并放置在同一个包中的类提供了自动查找的支持。

如果需要注册其他自定义PropertyEditor,有几种机制。大多数手动方式,通常不方便或不建议使用,简单的使用ConfigurableBeanFactory接口的registerCustomEditor()方法,假设你有BeanFactory引用。另一种,更方便的机制是使用称为CustomEditorConfigurer的特定Bean工厂后处理器。尽管Bean工厂后处理器能与BeanFactory实现一起使用,CustomEditorConfigurer有一个嵌套属性设置,因此强烈建议使用ApplicationContext,它可以部署在类似于任何其它Bean的地方,并自动检测和应用。

注意,所有Bean工厂和应用上下文自动使用一系列内置属性编辑器,通过使用一种叫做BeanWrapper的东西来处理属性转换。BeanWrapper注册的标准属性编辑器列在文章之前的小节。除此之外,ApplicationContext也覆盖或添加一个额外的编辑器处理适合于特定的应用程序上下文类型的资源查找方式。

标准JavaBeans PropertyEditor实例用于转换表示为字符串的属性值为真实的复杂类型属性。CustomEditorConfigurer,一个Bean工厂后处理器,可以用于方便的为ApplicationContext添加额外的PropertyEditor实例支持。

思考一个用户类ExoticType,和另一个类DependsOnExoticType需要ExoticType设置为一个属性:

package example;

public class ExoticType {

private String name;

public ExoticType(String name) {
        this.name = name;
    }
}

public class DependsOnExoticType {

private ExoticType type;

public void setType(ExoticType type) {
        this.type = type;
    }
}

当属性被设置时,我们想能够分配的属性类型为字符串,PropertyEditor将在幕后将字符串转换为一个实际ExoticType实例:

<bean id="sample" class="example.DependsOnExoticType">
    <property name="type" value="aNameForExoticType"/>
</bean>

PropertyEditor实现可能类似于:

// 转换字符串为ExoticType对象
package example;

public class ExoticTypeEditor extends PropertyEditorSupport {

public void setAsText(String text) {
        setValue(new ExoticType(text.toUpperCase()));
    }
}

最后,我们使用CustomEditorConfigurer为ApplicationContext注册新的PropertyEditor,这将能够在需要时使用它:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
        </map>
    </property>
</bean>

使用PropertyEditorRegistrars

另一种使用Spring容器注册属性编辑器的机制是创建并使用一个PropertyEditorRegistrars。该接口是特别有用的,当你需要在不同情况下时使用相同属性编辑器集:编写一个相应的注册器并在每种情况下重用。

PropertyEditorRegistrars和一个叫PropertyEditorRegistry的接口一起使用,一个实现Spring BeanWrapper(和DataBinder)的接口。PropertyEditorRegistrars特别方便,当联合CustomEditorConfigurer使用时,调用setPropertyEditorRegistrars(. .)暴露属性:PropertyEditorRegistrars以这种方式添加一个CustomEditorConfigurer可以很容易地在DataBinder和Spring MVC控制器之间共享。此外,它避免了需要同步自定义编辑器:PropertyEditorRegistrar预计将为每个bean创建新的PropertyEditor实例创建。

使用PropertyEditorRegistrar也许最好的用一个例子来阐述。首先,你需要创建自己的PropertyEditorRegistrar实现:

package com.foo.editors.spring;

public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

public void registerCustomEditors(PropertyEditorRegistry registry) {

// 期望创建新的PropertyEditor实例
        registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());

// 你可能在这里定义许多需要的自定义属性编辑器
    }
}

实现PropertyEditorRegistrar的例子见org.springframework.beans.support.REsourceEditorRegistrar。注意,在实现的registerCustomEditors(. .)方法中创建每个属性编辑器的新实例。

接下来,我们配置一个CustomEditorConfigurer并注入我们的CustomPropertyEditorRegistrar的实例:

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="propertyEditorRegistrars">
        <list>
            <ref bean="customPropertyEditorRegistrar"/>
        </list>
    </property>
</bean>

<bean id="customPropertyEditorRegistrar"
    class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>

最后,有点背离本文的重点,如果你使用Spring的MVC Web框架,联合使用PropertyEditorRegistrar和数据绑定控制器(例如SimpleFormController)非常方便。

public final class RegisterUserController extends SimpleFormController {

private final PropertyEditorRegistrar customPropertyEditorRegistrar;

public RegisterUserController(PropertyEditorRegistrar propertyEditorRegistrar) {
        this.customPropertyEditorRegistrar = propertyEditorRegistrar;
    }

protected void initBinder(HttpServletRequest request,
            ServletRequestDataBinder binder) throws Exception {
        this.customPropertyEditorRegistrar.registerCustomEditors(binder);
    }

// 注册用户的其它方法
}

这种风格的PropertyEditor注册可以导致简洁的代码(实现initBinder(..)是一条长线),并允许共同PropertyEditor登记代码封装在一个类,然后尽可能多的控制器之间需要共享。

时间: 2024-10-10 04:39:04

Spring 4.x Bean操作和BeanWrapper的相关文章

6.4 Bean操作和BeanWrapper类之一

由于6.1节篇幅较长,所以分为两篇文章来翻译 6.4 Bean操作和BeanWrapper类 org.springframework.beans包遵守Sun提供的JavaBeans标准.一个JavaBean需要满足以下条件:带有默认无参构造器的类,并且需要遵守一个命名规范,举个例子,比如有一个属性叫bingoMadness,那么必须要有一个setter方法setBingoMadness(..)和一个getter方法getBingoMadness().更多关于JavaBeans的信息,可以参考Su

6.4 Bean操作和BeanWrapper类之二

内置的PropertyEditor实现 Spring使用PropertyEditors的概念来实现Object和String的转换.如果你考虑它(PropertyEditors),那么有时候使用它来表示属性可能比对象本身更容易.比如,Date可以被表示成人类易读的方式(作为String '2008-14-09'),并且我们还可以将它转换成原始的Date(或者更好:将任何输入可读的日期,转换成Date对象).这可以通过注册自定义editors(java.beans.PropertyEditor类型

跟着柴毛毛学Spring(3)——简化Bean的配置

通过前面的学习.我们会感觉到对于一个有较多Bean的大项目,Spring的配置会比較复杂. 那么接下来我们就介绍怎样简化Spring的配置. 简化Spring的配置主要分为两类: 1. 自己主动装配 2. 自己主动扫描 以下就具体介绍这两种简化配置的方式. 自己主动装配 自己主动装配的种类 byName:依据属性的名字自己主动装配 byType:依据属性的类型自己主动装配 constructor:依据构造器的參数类型自己主动装配 autodetect:最佳自己主动装配.首先採用construct

Spring核心组件之Bean

Spring核心组件之Bean Bean的类层次结构 Bean的顶层接口是BeanFactory,在源码的位置是 /spring-framework/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java DefaultListableBeanFactory实现了所有的接口.但是为什么需要定义这么多的接口?为了区分在spring的内部对象的传递和转化过程中,对对象的数据访问所做的限制.Listab

spring容器中bean的作用范围

spring容器中bean的作用范围范围五类,bean的作用范围不同对bean的生命周期和创建方式也会产生一定的影响. singleton  以单例形式存在,容器中仅有一个bean实例. prototype  每次从容器中调用bean时,容器都创建一个新的实例,每次调用都相当于new一个新的对象. request 每次http请求都会创建一个新的bean. 此作用域仅适用于webApplicationContext环境. session 同一个session共享同一个bean,不同的sessio

Spring中管理Bean以及解析XML

Spring是分层的轻量级框架 以IoC(Inverse of Control 反转控制)和AOP(Aspect Oriented Programming 面向切面编程)为核心 应用Spring的好处: 方便解耦,简化开发 Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理 AOP编程的支持 Spring提供面向切面编程,可以方便的实现对程序进行权限拦截.运行监控等功能 声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无需手动编程 方便集成各种优秀框架

spring容器对bean生命周期的管理三中方式

spring容器对bean的生命周期管理主要在两个时间点:bean的初始化完成(包括属性值被完全注入),bean的销毁(程序结束,或者引用结束)方式一:使用springXML配置中的init-method="init" destroy-method="destory" 这个两个配置,可以实现两个时间点插入定制的操作.方式二: 使用spring提供的2个接口:InitializingBean,DisposableBean方式三:使用java注解:@PostConstr

spring加载bean实例化顺序

转载:http://blog.sina.com.cn/s/blog_525960510100ipwj.html http://blog.sina.com.cn/s/blog_6940cab30102uwma.html 问题来源: 有一个bean为 A,一个bean为B.想要A在容器实例化的时候的一个属性name赋值为B的一个方法funB的返回值. 如果只是在A里单纯的写着: private B b;private String name = b.funb(); 会报错说nullpointExce

Spring系列之bean的使用

一.Bean的定义 <bean id="userDao" class="com.dev.spring.simple.MemoryUserDao"/> 这是一个最简单的 Bean 定义.它类似于调用了语句: MemoryUserDao userDao = new MemoryUserDao(). id属性必须是一个有效的 XML ID,这意味着它在整个 XML 文档中必须唯一.它是一个 Bean 的"终身代号".同时你也可以用 name