Spring使用PropertyEditors的概念来实现Object和String的转换。如果你考虑它(PropertyEditors),那么有时候使用它来表示属性可能比对象本身更容易。比如,Date可以被表示成人类易读的方式(作为String '2008-14-09'),并且我们还可以将它转换成原始的Date(或者更好:将任何输入可读的日期,转换成Date对象)。这可以通过注册自定义editors(java.beans.PropertyEditor类型)来实现。通过在一个BeanWrapper上或者在给定的IoC容器中注册自定义的editors,告诉它(BeanWrapper)如何将属性转换为想要的类型。可以在java.bens包的Javadoc中阅读到更多关于PropertyEditors的信息。
Spring中几个用到属性编辑的例子:
1、设置beans属性的时候用到了PropertyEditors。当String作为一个属性值定义在XML文件中时,Spring将会(如果对应属性的set方法有一个类参数的话)使用ClassEditor来将该String参数解析成一个Class对象
2、在Spring MVC框架中使用多种PropertyEditors来解析HTTP请求参数,并且你也可以在CommandController的所有子类中手动绑定。
Spring拥有一系列的内置PropertyEditors来简化工作。下面列出了每一个PropertyEditor,并且他们都是在org.springframework.beans.propertyeditors包下。大多数,但并不是所有的(如下所示)PropertyEditor默认就被BeanWrapperImpl注册。不管它以何种方式注册,你都可以注册你自己的PropertyEditor来覆盖默认值。
下面对Spring中内置的PropertyEditor就不一一列举出来了,拿几个常用的来介绍介绍(译者添加)
ClassEditor:String和Class之间的转换,当然若String表示的类找不到,会报异常
CustomDateEditor:String和Date之间的转换,并未被默认注册。使用时必须手动注册,并且给定一个合适的格式化形式。
CustomNumberEditor:String和Number(Integer、Long、Float、Double)之间的转换。默认注册,但是可以被覆盖。
Spring使用java.beans.PropertyEditorManager来设置属性编辑器的搜索路径。搜索路径也包括sun.bean.editors,这个包里包括了比如Font,Color等大多数原始类型(primitive types)PropertyEditor的实现。需要注意的是,如果PropertyEditor类和标准的 JavaBeans在同一个包下,并且他们PropertyEditor的类名是JavaBeans的类名加上“Editor”,那么JavaBeans的基础结构会自动发现PropertyEditor的类(而无需你去注册它们)。比如,下面的类和包结构就足够将FooEditor类当做Foo类型的属性的PropertyEditor。
com
chank
pop
Foo
FooEditor // the PropertyEditorfor the Fooclass
在这里你还可以使用标准的BeanInfo JavaBeans机制。下面的例子是使用BeanInfo机制来精确地为一个相关类的属性注册一个或多个PropertyEditor实例。
com
chank
pop
Foo
FooBeanInfo // the BeanInfofor the Fooclass
下面是FooBeanInfo类的源代码。它会将Foo类的age属性关联到一个CustomNumberEditor。
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());
}
}
}
当使用String来设置bean属性时,Spring的IoC容器底层都会使用JavaBeans PropertyEditors来将Strings转换成该属性的复杂类型(译者注:也有可能是Integer等简单类型)。Spring预先注册了一部分自定义的PropertyEditors(比如,将一个classname转换成真正的Class对象)。另外,Java的标准JavaBeans PropertyEditor查找机制允许针对某个类的PropertyEditor只需要合适的命名并且和JavaBeans放在同一个包下,那么就会被自动找到。
如果还需要注册额外的自定义PropertyEditors,可以用到下面这些机制:最原始的方法,但并不推荐,因为不太方便,假设你有一个BeanFactory的引用,那么你只要调用ConfigurableBeanFactory接口的registerCustomEditor()方法就可以了。另一种稍微方便一点的方法,使用一个特殊的bean factory后置处理器 (post-processor)——CustomEditorConfigurer.尽管bean factory post-processors可以与BeanFactory的实现一起使用,但是CustomEditorConfigurer还是有一个嵌套的 属性安装,所以强烈建议与ApplicationContext一起使用,因为这样它可以使用与其他bean相似的方式来部署,并且可以被自动检测和应用。
注意,尽管所有的bean factories和application contexts都使用一个叫做BeanWrapper的东西来处理属性之间的转换,但它们还是会自动使用一些内建的property editors。关于BeanWrapper会注册的标准property editors,这在前面一节中已经列举过了。
另外,对于特定的应用上下文类型,ApplicationContexts也会在某种程度上重写或是添加一个额外数量的editors来处理资源的寻找。
标准的JavaBeans PropertyEditor实例是用来将String属性值转换成该属性真正的复杂类型。CustomEditorConfigurer是一个bean factory post-processor,它可以用来便捷地为ApplicationContext中额外注册的PropertyEditor实例来添加支持。
考虑一个类ExoticType和另一个拥有ExoticType属性的DependsOnExoticType类
package document.six.test;
public class ExoticType {
private String name;
public ExoticType(String name) {
this.name = name;
}
}
package document.six.test;
public class DependsOnExoticType {
private ExoticType type;
public void setType(ExoticType type) {
this.type = type;
}
public ExoticType getType() {
return type;
}
}
当一切都建立好之后,我们想要为ExoticType属性指定一个string,并且让PropertyEditor在底层将这个string转换成一个实际的ExoticType的实例。
PropertyEditor的实现看起来应该类似:(其实不需要这个PropertyEditor实现也可以,具体可以看这篇文章更详细)
package document.six.test;
import java.beans.PropertyEditorSupport;
//converts string representation to ExoticTypeobject
public class ExoticTypeEditor extends PropertyEditorSupport {
public void setAsText(String text) {
setValue(new ExoticType(text.toUpperCase()));
}
}
最后,我们使用CustomEditorConfigurer在ApplicationContext中来注册这个新的PropertyEditor,之后我们就可以在需要的时候使用它了。
创建并使用一个PropertyEditorRegistrar是另一个在Spring容器中注册property editors的方法。当你需要在一些不同的场景下使用同一组property editors,那么这个接口将会变得非常有用。比如:写一个通用的registrar然后在多种情况下重用。PropertyEditorRegistrars要和PropertyEditorRegistry一起工作,Spring的BeanWrapper(和DataBinder)实现了PropertyEditorRegistry这个接口。当你与CustomEditorConfigurer一起使用时,PropertyEditorRegistrars将会变得相当方便。CustomEditorConfigurer暴露了一个属性叫做setPropertyEditorRegistrars(..):用这种方式添加的PropertyEditorRegistrars可以很容易地与DataBinder和Spring MVC控制器共享。而且,它避免了自定义editors同步的问题:PropertyEditorRegistrar会为每个bean的创建尝试都创建一个新的PropertyEditor实例。
可能用一个例子可以更好的阐述如何使用一个PropertyEditorRegistrar。首先,你需要创建你自己的PropertyEditorRegistrar实现:
package document.six.test;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
public final class CustomPropertyEditorRegistrar implements
PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
// it is expected that new PropertyEditorinstances are created
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
// you could register as many custom property editors as are required
// here...
}
}
可以把org.springframework.beans.support.ResourceEditorRegistrar这个类当成一个PropertyEditorRegistrar实现的例子来学习。注意在这个实现中registerCustomEditors(..)方法是如何为每个property editor都创建一个新的实例的
下面我们配置一个CustomEditorConfigurer然后向其中注入一个CustomPropertyEditorRegistrar实例
最后,稍微偏离一下本章的主题,对那些使用Spring MVC web框架的朋友来说,使用PropertyEditorRegistrars和data-binding Controllers(比如SimpleFormController)一起工作将会非常方便。下面的例子就是在initBinder(..)的实现中使用一个PropertyEditorRegistrar(译者注:这里若是基于注解的Controller,那么在initBinder(..)方法上也需要打一个注解@InitBinder)
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注册代码被封装在一个类中,之后可以在许多Controllers中共享。