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'当然,第二种你也可以自定义属性编辑器的位置和名称。
注:如果使用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; } }
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... } }
而后是在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请求参数与对应类的转换。而且代码还可高度重用,非常好的一个设计!