一) BeanWrapper
BeanWrapper这个类一般不会被Spring的使用者直接调用,而是使用DataBinder和BeanFactory这两个类是间接被调用的。但是知道BeanWrapper的使用方式对于理解Spring的数据绑定机制还是十分用益的。下面我们就来看个BeanWrapper被直接调用的例子:
//首先是两个对象类Company Employee
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;
}
}
BeanWrapper company = BeanWrapperImpl(new Company());
// setting the company name..
company.setPropertyValue("name", "Some Company Inc.");
// ... can also be done like this:
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);
// ok, let's create the director and tie it to the company:
BeanWrapper jim = BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());
// retrieving the salary of the managingDirector through the company
Float salary = (Float) company.getPropertyValue("managingDirector.salary");
这便是BeanWrapper的核心作用——数据绑定!
二)PropertyEditor -- 属性编辑器
Spring的数据绑定非常强大。属性编辑器PropertyEditor的主要功能就是将外部的设置值转换为JVM内部的对应类型,所以属性编辑器其实就是一个类型转换器。它负责String与Object之间的转换。Spring本身已经定义了许多种类的属性编辑器,所以一些常用类型的转换已经不需要我们再关心了。我们关心的应该是如何自定义的PropertyEditor去满足某些特殊类的转换。
你可以有两种方式定义PropertyEditor,但不管采用哪种Spring都使用java.beans.PropertyEditorManager类来搜寻一个类所对应的属性编辑器。
1' 第一种当然是默认路径。
如图,FooEditor便是Foo的属性编辑器了。在同一Package下,Spring默认类名+"Editor" 便是该类的属性编辑器。
2'当然,第二种你也可以自定义属性编辑器的位置和名称。
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
</map>
</property>
</bean>
注:如果使用BeanFactory,用户需要手工调用registerCustomEditor(Class requiredType, PropertyEditor propertyEditor)方法注册自定义属性编辑器;如果使用ApplicationContext,则只需要在配置文件通过CustomEditorConfigurer注册就可以了。一般情况下,我们当然使用ApplicationContext。如上面这个例子,我们自定义了一个名叫ExoticTypeEditor的属性编辑器,它专门负责对ExoticType类的转换。具体的代码如下:
// converts string representation to ExoticType object
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
public void setAsText(String text) {
setValue(new ExoticType(text.toUpperCase()));
}
}
---------------------------------------------------------------
package example;
public class ExoticType {
private String name;
public ExoticType(String name) {
this.name = name;
}
}
注:一般地,我们要使用PropertyEditor时,并不直接实现此接口,而是通过继承实现此接口的java.beans.PropertyEditorSupport来简化我们的工作,在子类覆盖setAsText方法就可以了,setValue方法一般不直接使用,在setAsText方法中将字符串进行转换并产生目标对象以后,由调setAsText调用setValue来把目标对象注入到编辑器中。当然,你可用覆盖更多的方法来满足你的特殊要求。
这样当我们遇到如下情况时,我们的自定义属性编辑器便会被触发使用(反射机制):
public class DependsOnExoticType {
private ExoticType type;
public void setType(ExoticType type) {
this.type = type;
}
}
<bean id="sample" class="example.DependsOnExoticType">
<property name="type" value="aNameForExoticType"/>
</bean>
3' 刚说了只有两种定义属性编辑器的方式,这会儿咋又来第三种呢?呵呵,这种方式确实有点..."另类"?但它的作用非常大,它可以满足一个类中用到多个属性编辑器的情况:
还是首先讲讲命名规则,如图,FooBeanInfo便是Foo类的BeanInfo了。同一Package下,类名+"BeanInfo" 便是该类对应的属性编辑器,与之前不同的是,它说明了该了中需要使用到的一个或多个子属性编辑器。而它的代码实现如下:
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());
}
}
}
注:BeanInfo接口有一个常用的实现类:SimpleBeanInfo,一般情况下,可以通过扩展SimpleBeanInfo实现自己的功能。
三)PropertyEditorRegistrars
这个数据绑定接口结合Spring MVC模块一起使用效果非常好。结合一个例子就非常容易明白:
首先是声明和配置:
package com.foo.editors.spring;
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
// it is expected that new PropertyEditor instances are created
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
// you could register as many custom property editors as are required here...
}
}
<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中使用它:
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);
}
// other methods to do with registering a User
}
这样就实现了http请求参数与对应类的转换。而且代码还可高度重用,非常好的一个设计!