属性编辑器,即PropertyEditor-->Spring IoC

在Spring配置文件里,我们往往通过字面值为Bean各种类型的属性提供设置值:不管是double类型还是int类型,在配置文件中都对应字符串类型的字面值。BeanWrapper填充Bean属性时如何将这个字面值转换为对应的double或int等内部类型呢?我们可以隐约地感觉到一定有一个转换器在其中起作用,这个转换器就是属性编辑器。

“属性编辑器”这个名字可能会让人误以为是一个带用户界面的输入器,其实属性编辑器不一定非得有用户界面,任何实现java.beans.PropertyEditor接口的类都是属性编辑器。属性编辑器的主要功能就是将外部的设置值转换为JVM内部的对应类型,所以属性编辑器其实就是一个类型转换器。

PropertyEditor是JavaBean规范定义的接口,JavaBean规范中还有其他一些PropertyEditor配置的接口。为了彻底理解属性编辑器,必须对JavaBean中有关属性编辑器的规范进行学习,相信这些知识对学习和掌握Spring中的属性编辑器会大有帮助。

JavaBean的编辑器

Sun所制定的JavaBean规范,很大程度上是为IDE准备的——它让IDE能够以可视化的方式设置JavaBean的属性。如果在IDE中开发一个可视化应用程序,我们需要通过属性设置的方式对组成应用的各种组件进行定制,IDE通过属性编辑器让开发人员使用可视化的方式设置组件的属性。

一般的IDE都支持JavaBean规范所定义的属性编辑器,当组件开发商发布一个组件时,它往往将组件对应的属性编辑器捆绑发行,这样开发者就可以在IDE环境下方便地利用属性编辑器对组件进行定制工作。

JavaBean规范通过java.beans.PropertyEditor定义了设置JavaBean属性的方法,通过BeanInfo描述了JavaBean哪些属性是可定制的,此外还描述了可定制属性与PropertyEditor的对应关系。

BeanInfo与JavaBean之间的对应关系,通过两者之间规范的命名确立:对应JavaBean的BeanInfo采用如下的命名规范:<Bean>BeanInfo。如ChartBean对应的BeanInfo为ChartBeanBeanInfo;Car对应的BeanInfo为CarBeanInfo。当JavaBean连同其属性编辑器相同的组件注册到IDE中后,当在开发界面中对JavaBean进行定制时,IDE就会根据JavaBean规范找到对应的BeanInfo,再根据BeanInfo中的描述信息找到JavaBean属性描述(是否开放、使用哪个属性编辑器),进而为JavaBean生成特定开发编辑界面。

JavaBean规范提供了一个管理默认属性编辑器的管理器:PropertyEditorManager,该管理器内保存着一些常见类型的属性编辑器,如果某个JavaBean的常见类型属性没有通过BeanInfo显式指定属性编辑器,IDE将自动使用PropertyEditorManager中注册的对应默认属性编辑器。

由于JavaBean对应的属性编辑器等IDE环境相关的资源和组件需要动态加载,所以在纯Java的IDE中开发基于组件的应用时,总会感觉IDE反应很迟钝,不像Delphi、C++Builder一样灵敏快捷。但在Eclipse开发环境中,设计包括可视化组件的应用时却很快捷,原因是Eclipse没有使用Java的标准用户界面组件库,当然也就没有按照JavaBean的规范开发设计GUI组件了。

PropertyEditor

PropertyEditor是属性编辑器的接口,它规定了将外部设置值转换为内部JavaBean属性值的转换接口方法。PropertyEditor主要的接口方法说明如下:

  • ?  Object getValue():返回属性的当前值。基本类型被封装成对应的封装类实例;
  • ?  void setValue(Object newValue):设置属性的值,基本类型以封装类传入;
  • ?  String getAsText():将属性对象用一个字符串表示,以便外部的属性编辑器能以可视化的方式显示。缺省返回null,表示该属性不能以字符串表示;
  • ?  void setAsText(String text):用一个字符串去更新属性的内部值,这个字符串一般从外部属性编辑器传入;
  • ?  String[] getTags():返回表示有效属性值的字符串数组(如boolean属性对应的有效Tag为true和false),以便属性编辑器能以下拉框的方式显示出来。缺省返回null,表示属性没有匹配的字符值有限集合;
  • ?  String getJavaInitializationString():为属性提供一个表示初始值的字符串,属性编辑器以此值作为属性的默认值。

可以看出PropertyEditor接口方法是内部属性值和外部设置值的沟通桥梁。此外,我们可以很容易地发现该接口的很多方法是专为IDE中的可视化属性编辑器提供的:如getTags()、getJavaInitializationString()以及另外一些我们未此介绍的接口方法。

Java为PropertyEditor提供了一个方便类:PropertyEditorSupport,该类实现了PropertyEditor接口并提供默认实现,一般情况下,用户可以通过扩展这个方便类设计自己的属性编辑器。

BeanInfo

BeanInfo主要描述了JavaBean哪些属性可以编辑以及对应的属性编辑器,每一个属性对应一个属性描述器PropertyDescriptor。PropertyDescriptor的构造函数有两个入参: 
PropertyDescriptor(String propertyName, Class beanClass) ,其中propertyName为属性名;而beanClass为JavaBean对应的Class。

此外PropertyDescriptor还有一个setPropertyEditorClass(Class propertyEditorClass)方法,为JavaBean属性指定编辑器。BeanInfo接口最重要的方法就是:PropertyDescriptor[] getPropertyDescriptors() ,该方法返回JavaBean的属性描述器数组。

BeanInfo接口有一个常用的实现类:SimpleBeanInfo,一般情况下,可以通过扩展SimpleBeanInfo实现自己的功能。

一个实例

在本节中,我们来看一个具体属性编辑器的实例,该实例根据《Core Java Ⅱ》上的一个例子改编而成。

ChartBean是一个可定制图表组件,允许通过属性的设置定制图表的样式以得到满足各种不同使用场合要求的图表。我们忽略ChartBean的其他属性,仅关注其中的两个属性:

代码清单5-2  CharBean

Java代码  

  1. public class ChartBean extends JPanel{
  2. private int titlePosition = CENTER;
  3. private boolean inverse;
  4. //省略get/setter方法
  5. }

下面,我们为titlePosition属性提供一个属性编辑器。我们不去直接实现PropertyEditor,而是通过扩展PropertyEditorSupport这个方便类来定义我们的属性编辑器:

代码清单5-3  TitlePositionEditor

Java代码  

  1. import java.beans.*;
  2. public class TitlePositionEditor extends PropertyEditorSupport{
  3. private String[] options = { "Left", "Center", "Right" };
  4. //①代表可选属性值的字符串标识数组
  5. public String[] getTags() { return options; }
  6. //②代表属性初始值的字符串
  7. public String getJavaInitializationString() { return "" + getValue(); }
  8. //③将内部属性值转换为对应的字符串表示形式,供属性编辑器显示之用
  9. public String getAsText(){
  10. int value = (Integer) getValue();
  11. return options[value];
  12. }
  13. //④将外部设置的字符串转换为内部属性的值
  14. public void setAsText(String s){
  15. for (int i = 0; i < options.length; i++){
  16. if (options[i].equals(s)){
  17. setValue(i);
  18. return;
  19. }
  20. }
  21. }
  22. }

①处通过getTags()方法返回一个字符串数组,因此在IDE中该属性对应的编辑器将自动提供一个下拉框,下拉框中包含3个可选项:“Left”、“Center”、“Right”。而③和④处的两个方法分别完成属性值到字符串的双向转换功能。CharBean的inverse属性也有一个相似的编辑器InverseEditor,我们忽略不讲。

下面编写ChartBean对应的BeanInfo,根据JavaBean的命名规范,这个BeanInfo应该命名为ChartBeanBeanInfo,它负责将属性编辑器和ChartBean的属性挂钩起来:

代码清单5-4  ChartBeanBeanInfo

Java代码  

  1. import java.beans.*;
  2. public class ChartBeanBeanInfo extends SimpleBeanInfo{
  3. public PropertyDescriptor[] getPropertyDescriptors() {
  4. try{
  5. //①将TitlePositionEditor绑定到ChartBean的titlePosition属性中
  6. PropertyDescriptor titlePositionDescriptor
  7. = new PropertyDescriptor("titlePosition", ChartBean.class);
  8. titlePositionDescriptor.setPropertyEditorClass(TitlePositionEditor.class);
  9. //②将InverseEditor绑定到ChartBean的inverse属性中
  10. PropertyDescriptor inverseDescriptor
  11. = new PropertyDescriptor("inverse", ChartBean.class);
  12. inverseDescriptor.setPropertyEditorClass(InverseEditor.class);
  13. return new PropertyDescriptor[]{titlePositionDescriptor, inverseDescriptor};
  14. }
  15. catch (IntrospectionException e){
  16. e.printStackTrace();
  17. return null;
  18. }
  19. }
  20. }

在ChartBeanBeanInfo中,我们分别为ChartBean和titlePosition和inverse属性指定对应的属性编辑器。将ChartBean连同属性编辑器以及ChartBeanBeanInfo打成JAR包,使用IDE组件扩展管理功能注册到IDE中。这样,我们就可以像使用TextField、Checkbox等这些组对ChartBean进行可视化的开发设计工作了。下面是ChartBean在NetBeans IDE中的属性编辑器效果图,如图5-5所示。

ChartBean可设置的属性都列在属性查看器中,当单击titlePosition属性时,下拉框中列出了我们提供的3个选项。

Spring默认属性编辑器

Spring的属性编辑器和传统的用于IDE开发时的属性编辑器不同,它们没有UI界面,仅负责将配置文件中的文本配置值转换为Bean属性的对应值,所以Spring的属性编辑器并非传统意义上的JavaBean属性编辑器。

Spring为常见的属性类型提供了默认的属性编辑器。从图5-4中,我们可以看出BeanWrapperImpl类扩展了PropertyEditorRegistrySupport类,Spring在PropertyEditor RegistrySupport中为常见属性类型提供了默认的属性编辑器,这些“常见的类型”共32个,可分为3大类,总结如下:

表5-1  Spring提供的默认属性编辑器

类    别 说    明
基础数据类型 分为几个小类: 1)基本数据类型,如:boolean、byte、short、int等; 2)基本数据类型封装类,如:Long、Character、Integer等; 3)两个基本数据类型的数组,char[]和byte[]; 4)大数类,BigDecimal和BigInteger
集合类 为5种类型的集合类Collection、Set、SortedSet、List和SortedMap提供了编辑器
资源类 用于访问外部资源的8个常见类Class、Class[]、File、InputStream、Locale、Properties、Resource[]和URL  

PropertyEditorRegistrySupport中有两个用于保存属性编辑器的Map类型变量:

  • ?  defaultEditors:用于保存默认属性类型的编辑器,元素的键为属性类型,值为对应的属性编辑器实例;
  • ?  customEditors:用于保存用户自定义的属性编辑器,元素的键值和defaultEditors相同。

PropertyEditorRegistrySupport通过类似以下的代码定义默认属性编辑器:

Java代码  

  1. this.defaultEditors.put(char.class, new CharacterEditor(false));
  2. this.defaultEditors.put(Character.class, new CharacterEditor(true));
  3. this.defaultEditors.put(Locale.class, new LocaleEditor());
  4. this.defaultEditors.put(Properties.class, new PropertiesEditor());

这些默认的属性编辑器解决常见属性类型的注册问题,如果用户的应用包括一些特殊类型的属性,且希望在配置文件中以字面值提供配置值,那么就需要编写自定义属性编辑器并注册到Spring容器中。这样,Spring才能将配置文件中的属性配置值转换为对应的属性类型值。

自定义属性编辑器

Spring大部分默认属性编辑器都直接扩展于java.beans.PropertyEditorSupport类,用户也可以通过扩展PropertyEditorSupport实现自己的属性编辑器。比起用于IDE环境的属性编辑器来说,Spring环境下使用的属性编辑器的功能非常单一:仅需要将配置文件中字面值转换为属性类型的对象即可,并不需要提供UI界面,因此仅需要简单覆盖PropertyEditorSupport的setAsText()方法就可以了。

一个实例

我们继续使用第4章中Boss和Car的例子,假设我们现在希望在配置Boss时,不通过引用Bean的方式注入Boss的car属性,而希望直接通过字符串字面值提供配置。为了方便阅读,这里再次列出Boss和Car类的简要代码:

代码清单5-5  Car

Java代码  

  1. package com.baobaotao.editor;
  2. public class Car {
  3. private int maxSpeed;
  4. public String brand;
  5. private double price;
  6. //省略get/setter
  7. }

代码清单5-6  Boss

Java代码  

  1. package com.baobaotao.editor;
  2. public class Boss {
  3. private String name;
  4. private Car car = new Car();
  5. //省略get/setter
  6. }

Boss有两个属性:name和car,分别对应String类型和Car类型。Spring拥有String类型的默认属性编辑器,因此对于String类型的属性我们不用操心。但Car类型是我们自定义的类型,要配置Boss的car属性,有两种方案:

  • 1)在配置文件中为car专门配置一个<bean>,然后在boss的<bean>中通过ref引用car Bean,这正是我们上一章中所用的方法;
  • 2)为Car类型提供一个自定义的属性编辑器,这样,我们就通过字面值为Boss的car属性提供配置值。

第一种方案是常用的方法,但是在有些情况下,这种方式需要将属性对象一步步肢解为最终可以用基本类型表示的Bean,使配置文件变得不够清晰,直接为属性类提供一个对应的自定义属性编辑器可能会是更好的替代方案。

现在,我们来为Car编写一个自定义的属性编辑器,其代码如下所示:

代码清单5-7  CustomCarEditor

Java代码  

  1. package com.baobaotao.editor;
  2. import java.beans.PropertyEditorSupport;
  3. public class CustomCarEditor extends PropertyEditorSupport {
  4. //①将字面值转换为属性类型对象
  5. public void setAsText(String text){
  6. if(text == null || text.indexOf(",") == -1){
  7. throw new IllegalArgumentException("设置的字符串格式不正确");
  8. }
  9. String[] infos = text.split(",");
  10. Car car = new Car();
  11. car.setBrand(infos[0]);
  12. car.setMaxSpeed(Integer.parseInt(infos[1]));
  13. car.setPrice(Double.parseDouble(infos[2]));
  14. //②调用父类的setValue()方法设置转换后的属性对象
  15. setValue(car);
  16. }
  17. }

CustomCarEditor很简单,它仅覆盖PropertyEditorSupport便利类的setAsText(String text)方法,该方法负责将配置文件以字符串提供的字面值转换为Car对象。字面值采用逗号分隔的格式同时为brand、maxSpeed和price属性值提供设置值,setAsText()方法解析这个字面值并生成对应的Car对象。由于我们并不需要将Boss内部的car属性反显到属性编辑器中,因此不需要覆盖getAsText()方法。

注册自定义的属性编辑器

在IDE环境下,自定义属性编辑器在使用之前必须通过扩展组件功能进行注册,在Spring环境中也需要通过一定的方法注册自定义的属性编辑器。

如果使用BeanFactory,用户需要手工调用registerCustomEditor(Class requiredType, PropertyEditor propertyEditor)方法注册自定义属性编辑器;如果使用ApplicationContext,则只需要在配置文件通过CustomEditorConfigurer注册就可以了。CustomEditorConfigurer实现BeanFactoryPostProcessor接口,因此是一个Bean工厂后处理器。我们知道Bean工厂后处理器在Spring容器加载配置文件并生成BeanDefinition半成品后就会被自动执行。因此CustomEditorConfigurer有容器启动时有机会注入自定义的属性编辑器。下面的配置片断定义了一个CustomEditorConfigurer:

Xml代码  

  1. <!--①配置自动注册属性编辑器的CustomEditorConfigurer -->
  2. <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
  3. <property name="customEditors">
  4. <map>
  5. <!--②-1属性编辑器对应的属性类型-->
  6. <entry key="com.baobaotao.editor.Car">
  7. <!--②-2对应的属性编辑器Bean -->
  8. <bean class="com.baobaotao.editor.CustomCarEditor" />
  9. </entry>
  10. </map>
  11. </property>
  12. </bean>
  13. <bean id="boss" class="com.baobaotao.editor.Boss">
  14. <property name="name" value="John"/>
  15. <!--③该属性将使用②处的属性编辑器完成属性填充操作-->
  16. <property name="car" value="红旗CA72,200,20000.00"/>
  17. </bean>

在①处,我们定义了用于注册自定义属性编辑器的CustomEditorConfigurer,Spring容器将通过反射机制自动调用这个Bean。CustomEditorConfigurer通过一个Map属性定义需要自动注册的自定义属性编辑器。在②处,我们为Car类型指定了对应属性编辑器CustomCarEditor,注意键是属性类型,而值是对应的属性编辑器Bean,而不是属性编辑器的类名。

最精彩的部分当然是③处的配置,我们原来通过一个<bean>元素标签配置好car Bean,然后在boss的<bean>中通过ref引用car Bean,但是现在我们直接通过value为car属性提供配置。BeanWrapper在设置boss的car属性时,它将检索自定义属性编辑器的注册表,当发现Car属性类型拥有对应的属性编辑器CustomCarEditor时,它就会利用CustomCarEditor将“红旗CA72,200,20000.00”转换为Car对象。

引用

按照JavaBeans的规范,JavaBeans的基础设施会在JavaBean相同类包下查找是否存在<JavaBean>Editor的类,如果存在,自动使用<JavaBean>Editor作为该JavaBean的PropertyEditor。 
如com.baobaotao.domain.UserEditor会自动成为com.baobaotao.domain.User对应的PropertyEditor。Spring也支持这个规范,也即如果采用这种规约命令PropertyEditor,就无须显式在CustomEditorConfigurer中注册了,Spring将自动查找并注册这个PropertyEditor。

另:Spring 3.0除支持PropertyEditor外,还在核心包中引入了自建的ConversionService,它提供了更为强大的类型转换的能力,可以完成任意类型之间的转换,还可以在转换过程中参考目标对象所在宿主类的上下文信息。Spring的类型转换同时支持PropertyEdito和ConversionService。

这些文章摘自于我的《Spring 3.x企业应用开发实战》,我将通过连载的方式,陆续在此发出。欢迎大家讨论。

时间: 2024-11-01 17:57:06

属性编辑器,即PropertyEditor-->Spring IoC的相关文章

(spring-第13回【IoC基础篇】)PropertyEditor(属性编辑器)--实例化Bean的第五大利器

上一篇讲到JavaBeans的属性编辑器,编写自己的属性编辑器,需要继承PropertyEditorSupport,编写自己的BeanInfo,需要继承SimpleBeanInfo,然后在BeanInfo中把特定的属性编辑器和需要编辑的属性绑定起来(详情请查看上一篇). Spring的属性编辑器仅负责将配置文件中的字面值转换成Bean属性的对应值.(而JavaBean的属性编辑器能够通过界面来手动设置bean属性的值).如果属性的类型不同,转换的方法就不同.正如javabean的属性编辑器一样,

Spring IOC 和 AOP概述

IoC(控制反转,(Inversion of Control):本来是由应用程序管理的对象之间的依赖关系,现在交给了容器管理,这就叫控制反转,即交给了IoC容器,Spring的IoC容器主要使用DI方式实现的. 不需要主动查找,对象的查找.定位和创建全部由容器管理 DI(Dependency Injection) : IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如: setter 方法)接受来自如容器的资源注入. 相对于 IOC 而言,这种表述更直接 1.大量减少了Factory和

(spring-第12回【IoC基础篇】)JavaBean的属性编辑器

在spring实例化bean的最后阶段,spring利用属性编辑器将配置文件中的文本配置值转换为bean属性的对应值,例如: 代码0011 <bean id="car" class="com.mesopotamia.test1.Car" 2 p:name="汽车" 3 p:brand="宝马" 4 p:maxSpeed="200"/> 上面是文本配置, 再看bean: 代码0021 public

Spring常用属性的注入及属性编辑器

对于对象的注入,我们使用ref方式,可以指定注入的对象,下面看下对于基本类型的注入,以及当spring无法转换基本类型进行注入时,如何编写一个类似转换器的东西来完成注入. 一,基本类型的注入 下面写一个简单类,来看下spring中对于基本类型的注入: <bean id="bean1" class="com.shuitian.spring.Bean1"> <!-- <property name="strValue" valu

spring IOC快速入门,属性注入,注解开发

我们使用spring框架也会使用到配置文件,我们需要在src下创建一个关于spring的配置文件,一般情况名称叫applicationContext.xml 基本约束: <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> IOC快速入门 inversion of Controller

Spring经常使用属性的注入及属性编辑器

对于对象的注入,我们使用ref方式,能够指定注入的对象.以下看下对于基本类型的注入.以及当spring无法转换基本类型进行注入时,怎样编写一个相似转换器的东西来完毕注入. 一.基本类型的注入 以下写一个简单类.来看下spring中对于基本类型的注入: <bean id="bean1" class="com.shuitian.spring.Bean1"> <!-- <property name="strValue" valu

Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反射创建一个原始的 bean 对象,然后再向这个原始的 bean 对象中填充属性.对于填充属性这个过程,简单点来说,JavaBean 的每个属性通常都有 getter/setter 方法,我们可以直接调用 setter 方法将属性值设置进去.当然,这样做还是太简单了,填充属性的过程中还有许多事情要做.

Spring属性编辑器详解

1.常见的属性的注入:int,string,list,set,map 2.什么是属性编辑器及作用? (1)将spring配置文件中的字符串转换为相应的java对象 (2)spring内置了一些属性编辑器,也可以自定义属性编辑器 3.如果自定义属性编辑器 (1)继承propertyEditorSupport (2)重写setAsText 方法 (3)使用 setValue 完成属性对象设置 下面通过实例来说明类属性自定义动态加载 工程截图: 工程说明: 1.Education.java 自定义属性

SpringMVC类型转换器、属性编辑器

对于MVC框架,参数绑定一直觉得是很神奇很方便的一个东西,在参数绑定的过程中利用了属性编辑器.类型转换器 参数绑定流程 参数绑定:把请求中的数据,转化成指定类型的对象,交给处理请求的方法 请求进入到DisptacherServlet,卸下请求中的数据 DisptacherServlet将请求中的数据发送给Controller 获取Controller需要接收的参数类型,将参数类型和请求数据发送给DataBinder DataBinder将参数类型和请求数据再发给TypeConverter,由Ty