Spring框架学习笔记-属性编辑器

因为Spring注入的时候,属性值都是文本类型的,注入到Bean中肯定需要类型转换的。属性编辑器就是用于属性值与文本值之间的互相转换,每个属性编辑器仅仅适用于某一类属性。Spring中已经为我们注册了很多的编辑器,如果Spring提供的属性编辑器不能够满足需求,那么我们就需要进行自定义属性编辑器并注册到Spring中了。
“属性编辑器”这个名字可能会让人误以为是一个带用户界面的输入器,其实属性编辑器不一定非得有用户界面,任何实现java.beans.PropertyEditor接口的类都是属性编辑器
属性编辑器的主要功能就是将外部的设置值转换为JVM内部的对应类型,所以属性编辑器其实就是一个类型转换器。

1.JavaBean编辑器简介

JavaBean规范通过对java.beans.PropertyEditor定义设置了JavaBean属性的方法,通过BeanInfo描述了JavaBean哪些属性是可定制的,此外还描述了可定制属性与PropertyEditor的对应关系。
BeanInfo与JavaBean之间的对应关系,通过两者之间规范的命名确立:< Bean >BeanInfo。如ChartBean对应的BeanInfo为ChartBeanBeanInfo。
当在开发界面对JavaBean进行定制的时候,IDE就会根据JavaBean的规范找到对应的BeanInfo,再根据BeanInfo中的描述信息找到JavaBean属性描述(是否开放,使用哪一个属性编辑器),进而为JavaBean生成特定开打编辑界面。
JavaBean规范提供了一个管理默认属性编辑器的管理器:PropertyEditorManager,该管理器内保存着一些常见类型的属性编辑器,如果某个JavaBean的常见类型属性没有通过BeanInfo显式指定属性编辑器,IDE将自动使用PropertyEditorManager中注册的对应默认属性编辑器。

PropertyEditor接口

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

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

可以看出,PropertyEditor接口方法是内部属性值和外部设置值的沟通桥梁。此外,我们可以很容易地发现该接口的很多方法是专门为IDE中的可视化属性编辑器提供的。
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[] getPropertyDescriptor(),该方法返回JavaBean的属性描述器数组。
BeanInfo接口有一个常见的实现类:SimpleBeanInfo,一般情况下,可以通过扩展SimpleBeanInfo实现自己的功能。

一个实例

我们来看一个具体属性编辑器的实例。
ChartBean是一个可定制图标组件,允许通过属性的设置定制图标的样式得以满足各种不同使用场景要求的图表。我们忽略ChartBean的其他属性,仅关注其中的两个属性:

public class ChartBean extends JPanel{
	private int titlePosition=CENTER;
	private boolean inverse;
}

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

import java.beans.*;
public class TitlePositionEditor extends PropertyEditorSupport{
	private String[] options={"Left","Center","Right"};
	public String[] getTags(){return options;}//返回表示有效属性值的字符串数组
	public String getJavaInitializationString(){return ""+getValue();}//为属性提供一个表示初始值的字符串
	public String getAsTest(){/将属性对象用一个字符串表示,以便外部的属性编辑器能以可视化的方式显示。缺省值返回null,表示该属性不能以字符串表示
		int value=(Integer)getValue();//属性对象字符串化
		return options[value];
	}
	public void setAsText(String s){//用一个字符串去更新属性的内部值,这个字符串一般从外部属性编辑器传入
		for(int i=0;i<options.length;i++){
			if(option[i].equals(s)){
				setValue(i);
				return;
			}
		}
	}
}

我们注意到,通过getTags()方法返回一个字符串数组,因此在IDE中该属性对应的编辑器将自动提供一个下拉框,下拉框中包含三个可选项:“Left”、“Center"以及"Right”。而getAsTest()以及setAsTest()这两个方法则分别完成了属性值到字符串的双向转换功能。
下面编写ChartBean对应的BeanInfo,根据JavaBean的命名规范,这个BeanInfo应该命名为ChartBeanBeanInfo,它负责将属性编辑器和ChartBean的属性挂钩起来:

import java,beans.*;
public class ChartBeanBeanInfo extends SimpleBeanInfo{
	public PropertyDescriptor[] getPropertyDescriptors(){
		try{
			PropertyDescriptor titlePositionDescriptor=new PropertyDescriptor("titlePosition",ChartBean.class);
			titlePositionDescriptor.setPropertyEditorClass(TitlePositionEditor.class);
			PropertyDescriptor inverseDescriptor=new PropertyDescriptor("inverseEditor",ChartBean.class);
			inverseDescriptor.setPropertyEditorClass(InverseEditor.class);
			return new PropertyDescriptor[]{titlePositionDescriptor,inverseDescriptor};
		}
		catch(IntrosprctionException e){
			e.printStackTrace();
			return null;
		}
	}
}

2.Spring默认属性编辑器

Spring的属性编辑器和传统的用于IDE开发时的属性编辑器不同,它没有UI界面,仅负责将配置中的文本配置值转换为Bean属性的对应值,所以Spring的属性编辑器并非传统意义上的JavaBean属性编辑器。
Spring为常见的属性类型提供了默认的属性编辑器。
PropertyEditorRegistrySupport中有两个用于保存属性编辑器的Map类型变量:
1.defaultEditors:用于保存默认属性类型的编辑器,元素的键为属性类型,值为对应的属性编辑器实例。
2.customEditors:用于保存用户自定义的属性编辑器,元素的键值和defaultEditors相同。
PropertyEditorRegistrySupport通过类似以下的代码定义默认的属性编辑器。

this.defaultEditors.put(char,new CharacterEditor(false));
this.defaultEditors.put(Character,new CharacterEditor(true));
this.defaultEditors.put(Locale.class,new CharacterEditor());
this.defaultEditors.put(Properties.class,new CharacterEditor());

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

3.自定义属性编辑器

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

一个实例

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

package com.baobaotao.editor;
public class Car{
	private int maxSpeed;
	public String brand;
	private double price;
	//省略get和set方法
}
package com.baobaotao.editor;
public class Boss{
	private String name;
	private Car car=new Car();
	//上同
}

Boss有两个属性:name和car,分别对应String类型和Car类型。Spring拥有String类型的默认属性编辑器,因此对于String类型的属性我们不必操心。但是Car类型是我们自己定义的类型,要配置Boss的car属性,有两种方案:
方案一是在配置文件中为car专门设置一个< bean >,然后再boss的< bean >中通过ref引用carBean,这正是我们前面所说的方法。
方案二是为Car类型提供一个自定义的属性编辑器。
第一中方案是常用的方法,但是在有些情况下,这种方式需要将属性对象一步步肢解为最终可以用基本类型表示的Bean,这使得配置文件变得不够清晰,直接为属性类提供一个对应的自定义属性编辑器可能会是一个更好的配置方案。
现在,我们来为Car编写一个自定义的属性编辑器,其代码如下所示:

package com.baobaotao.editor;
import java.beans.PropertyEditorSupport;
public class CustomCarEditor extends PropertyEditorSupport{
	public void setAsText(String text){
		if(text==null||text.indexOf(",")==-1){
			throw new IllegalArgumentException("设置的字符串格式不正确");
		}
		String [] infos=text.split(",");
		Car car=new Car();
		car.setBrand(info[0]);
		car.setMaxSpeed(integer.parseInt(infos[1]));
		car.setPrice()
		setValue(car);//调用父类的setValue方法设置转换后的属性对象
	}
}

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

注册自定义的属性编辑器

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

<!-- 1.配置自动注册属性编辑器的CustomEditorConfigurer-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<!-- 2
			<entry key="com.baobaotao.editor.Car">
				<bean class="com.baobaotao.editor.CustomCarEditor"/>
			</entry>
		</map>
	</property>
</bean>
<bean id="boss" class="com.baobaotao.editor.Boss">
	<property name="name" value="John"/>
	<property name="car" value="红旗CA72,200,20000.00"/>
</bean>

在1处,我们定义了用于注册自定义属性编辑器的CustomEditorConfigurer,Spring容器将通过反射机制自动调用这个Bean。CustomEditorConfigurer通过一个Map属性定义需要自动注册的自定义属性编辑器。在2处,我们为Car类型制定了对应的属性编辑器CustomCarEditor,注意键是属性类型,而值是对应的属性编辑器Bean,而不是属性编辑器的类名。
最精彩的部分当然是3处的配置,我们原来通过一个< bean >元素标签配置好car Bean,然后再boss的< bean >中通过ref引用car Bean,但是现在我们直接通过value为car属性提供配置。BeanWrapper在设置boss的car属性时,它将检索自定义属性编辑器的注册表,当发现Car属性有对应的属性编辑器CustomCarEditor时,它就会利用CustomCarEditor将"红旗CA72,200,20000.00"转换为Car对象。
最后,我们还是需要说明一下,按照JavaBean的规范,其会在JavaBean相同类包下查找是否存在< JavaBean >Editor的类,如果存在,自动使用< JavaBean >Editor作为该JavaBean的PropertyEditor。
如com.baobaotao.domain.UserEditor会自动成为com.baobaotao.domain.User对应的PropertyEditor。Spring也支持这个规范,也即如果采用了这种规约命令的PropertyEditor,就无需显式地在CustomEditorConfigurer中注册了,Spring会自动查找并注册这个PropertyEditor。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值