属性编辑器PropertyEditor

在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采用如下的命名规范:BeanInfo。如ChartBean对应的BeanInfo为ChartBeanBeanInfo,Car对应的BeanInfo为CarBeanInfo。当JavaBean、其各属性的编辑器、和BeanInfo信息一起注册到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的其他属性,仅关注其中的两个属性:

import javax.swing.JPanel;  
import static javax.swing.border.TitledBorder.CENTER;  public class ChartBean extends JPanel{    private int titlePosition = CENTER;    private boolean inverse;    //省略get/setter方法    
}    

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

import java.beans.*;  public class TitlePositionEditor extends PropertyEditorSupport {  private final String[] options = {"Left", "Center", "Right"};  //1. 代表可选属性值的字符串标识数组  @Override  public String[] getTags() {  return options;  }  //2. 代表属性初始值的字符串  @Override  public String getJavaInitializationString() {  return "" + getValue();  }  //3. 将内部属性值转换为对应的字符串表示形式,供属性编辑器显示之用  @Override  public String getAsText() {  int value = (Integer) getValue();  return options[value];  }  //4. 将外部设置的字符串转换为内部属性的值  @Override  public void setAsText(String s) {  for (int i = 0; i < options.length; i++) {  if (options[i].equals(s)) {  setValue(i);  return;  }  }  }  
}  

1处通过getTags()方法返回一个字符串数组,因此在IDE中该属性对应的编辑器将自动提供一个下拉框,下拉框中包含3个可选项:“Left”、“Center”、“Right”。而3和4处的两个方法分别完成属性值到字符串的双向转换功能。CharBean的inverse属性也有一个相似的编辑器InverseEditor,我们忽略不讲。
  下面编写ChartBean对应的BeanInfo,根据JavaBean的命名规范,这个BeanInfo应该命名为ChartBeanBeanInfo,它负责将属性编辑器和ChartBean的属性挂钩起来:

import java.beans.*;  public class ChartBeanBeanInfo extends SimpleBeanInfo {  @Override  public PropertyDescriptor[] getPropertyDescriptors() {  try {  //1. 将TitlePositionEditor绑定到ChartBean的titlePosition属性中      PropertyDescriptor titlePositionDescriptor = new PropertyDescriptor("titlePosition", ChartBean.class);  titlePositionDescriptor.setPropertyEditorClass(TitlePositionEditor.class);  //2. 将InverseEditor绑定到ChartBean的inverse属性中              PropertyDescriptor inverseDescriptor = new PropertyDescriptor("inverse", ChartBean.class);  inverseDescriptor.setPropertyEditorClass(InverseEditor.class);  return new PropertyDescriptor[]{titlePositionDescriptor, inverseDescriptor};  } catch (IntrospectionException e) {  e.printStackTrace();  return null;  }  }  
}  

在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提供的默认属性编辑器
在这里插入图片描述
PropertyEditorRegistrySupport中有两个用于保存属性编辑器的Map类型变量:
defaultEditors:用于保存默认属性类型的编辑器,元素的键为属性类型,值为对应的属性编辑器实例;
customEditors:用于保存用户自定义的属性编辑器,元素的键值和defaultEditors相同。
  PropertyEditorRegistrySupport通过类似以下的代码定义默认属性编辑器:

this.defaultEditors.put(char.class, new CharacterEditor(false));    
this.defaultEditors.put(Character.class, new CharacterEditor(true));    
this.defaultEditors.put(Locale.class, new LocaleEditor());    
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类的简要代码:

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

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

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

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:

<!--①配置自动注册属性编辑器的CustomEditorConfigurer -->    
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">    <property name="customEditors">    <map>    <!--②-1属性编辑器对应的属性类型-->    <entry key="com.baobaotao.editor.Car">     <!--②-2对应的属性编辑器Bean -->    <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>    

在①处,我们定义了用于注册自定义属性编辑器的CustomEditorConfigurer,Spring容器将通过反射机制自动调用这个Bean。CustomEditorConfigurer通过一个Map属性定义需要自动注册的自定义属性编辑器。在②处,我们为Car类型指定了对应属性编辑器CustomCarEditor,注意键是属性类型,而值是对应的属性编辑器Bean,而不是属性编辑器的类名。
  最精彩的部分当然是③处的配置,我们原来通过一个元素标签配置好car Bean,然后在boss的中通过ref引用car Bean,但是现在我们直接通过value为car属性提供配置。BeanWrapper在设置boss的car属性时,它将检索自定义属性编辑器的注册表,当发现Car属性类型拥有对应的属性编辑器CustomCarEditor时,它就会利用CustomCarEditor将“红旗CA72,200,20000.00”转换为Car对象。
  引用:

按照JavaBeans的规范,JavaBeans的基础设施会在JavaBean相同类包下查找是否存在Editor的类,如果存在,自动使用Editor作为该JavaBean的PropertyEditor。
  如com.baobaotao.domain.UserEditor会自动成为com.baobaotao.domain.User对应的PropertyEditor。Spring也支持这个规范,也即如果采用这种规约命令PropertyEditor,就无须显式在CustomEditorConfigurer中注册了,Spring将自动查找并注册这个PropertyEditor。
  另:Spring 3.0除支持PropertyEditor外,还在核心包中引入了自建的ConversionService,它提供了更为强大的类型转换的能力,可以完成任意类型之间的转换,还可以在转换过程中参考目标对象所在宿主类的上下文信息。Spring的类型转换同时支持PropertyEditor和ConversionService。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/249556.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

邮箱验证

public class Emailstandard { /* * 以数字或字母开头 * 之前可以含有数字,字母,下划线,点 * 有且只有一个 * 之后只能含有数字,字母 * 必须以.com或者.cn结尾 * */ public static void main(String[] args) { Scanner sca new Scanner(…

python第二十八课——编码小常识

2.内存和硬盘&#xff1a;内存&#xff1a;计算机硬件组成部分之一&#xff0c;它是一个容器&#xff0c;用来存储数据&#xff1b;处理数据速度快&#xff0c;存储数据量小&#xff1b;断电死机数据会丢失&#xff0c;短暂性存储数据硬盘&#xff1a;计算机硬件组成部分之一&a…

Javadoc 使用详解

很多程序对Javadoc都不重视&#xff0c;认识不到Javadoc的作用&#xff0c;很多人都是这样认为的&#xff1a;“我只要写好功能就够了&#xff0c;写Javadoc太浪费时间&#xff0c;也没啥作用&#xff0c;还不如用写Javadoc的时间再多些个功能呢&#xff01;”&#xff0c;我们…

Linux下查看当前文件大小的命令

1、ls -lht 列出每个文件的大小和当前目录所有文件大小总和 2、du -sh * 列出当前文件夹下的所有子文件的大小 看你需要啥样的&#xff0c;自己来吧 转载于:https://www.cnblogs.com/xbxxf/p/9619818.html

(13)UniquePathIII

一、问题描述 给定一个二维数组。 数组只有一个元素是1&#xff0c;是起点数组只有一个元素是2&#xff0c;是终点数组中的0是必须经过的地方数组中的-1是障碍不可通过从起始点到终点一共有多少路径&#xff1f; 二、思路 DFS 三、Code 1 package algorithm;2 3 /**4 * Create…

Spring IOC-BeanFactory的继承体系结构

本文主要介绍BeanFactory以及它的各种继承层级的接口、抽象类及实现类&#xff0c;因为内容很多&#xff0c;所以这里不介绍ApplicationContext继承体系下的类&#xff08;虽然ApplicationContext本质上也是BeanFactory&#xff0c;但是毕竟这这是我们平时接触最多的两种类别&a…

deepin15.7挂载/home到单独的分区:

1、首先打开Gpart分区编辑器&#xff0c;找一个空闲的分区&#xff0c;调整好分区大小&#xff0c;格式化成ext4格式。 具体步骤为首先unmount所用到的盘&#xff0c;然后右击该盘选择format to ext4&#xff0c;最后点击apply提交修改 2、记录下分区的路径&#xff0c;比如 /d…

Java使用Redis实现分布式锁来防止重复提交问题

如何用消息系统避免分布式事务&#xff1f; - 少年阿宾 - BlogJavahttp://www.blogjava.net/stevenjohn/archive/2018/01/04/433004.html 【请求去重】java基于分布式锁解决重复请求问题 - qq_41793222的博客 - CSDN博客https://blog.csdn.net/qq_41793222/article/details/830…

【PHP】xampp配置多个监听端口和不同的网站目录(转)

转自&#xff1a;https://blog.csdn.net/cc1314_/article/details/75646344 windows下使用xampp配置多个监听端口和不同的网站目录 一&#xff1a;配置Apache文件httpd.conf打开Apache的配置文件httpd.conf&#xff0c;可以通过点击xampp的Apache的config下的Apache(httpd.conf…

本地连接虚拟机 Oracle数据库 报ORA-12541:TNS:no listener

一、环境 本机环境&#xff1a;win10,pl/sql Developer 虚拟机环境&#xff1a;win10&#xff0c;oracle 11g 1.本机和虚拟机互相ping都可以ping通。 2.虚拟机监听程序已启动。 二、配置文件 1.本机 tnsname.ora 配置文件 local (DESCRIPTION   (ADDRESS_LIST   (ADDR…

Java消息中间件

1.概述 中间件 非底层操作系统软件&#xff0c;非业务应用软件&#xff0c;不是直接给最终用户使用的&#xff0c;不能直接给客户带来价值的软件统称为中间件。 消息中间件 管制关注于数据的发送和接收&#xff0c;利用高效可靠的异步消息传递机制集成分布式系统。 优点 ① 解…

form 源码刨析

def clean_name(self) value self.cleaned_data.get(name) if "金-瓶-梅" not in value: raise ValidationError("不符合要求") return value 重写clean方法 转载于:https://www.cnblogs.com/wuheng-123/p/9623289.html

两道面试题

fi [] for i in range(3):def foo(x):print(x i) #由于函数在这时还没有执行&#xff0c;在这里的i&#xff0c;指向的还是同一个IP地址&#xff0c;所以都是2.fi.appent(foo) for f in fi:f(2)答案&#xff1a;4&#xff0c;4&#xff0c;4 a [0, 1, 2, 3, 4] print(a[-6:6…

uiautomator2进阶

点击控件的具体位置 d(text"Settings").click(offset(0.5, 0.5)) 点击控件的中间位置 d(text"Settings").click(offset(0, 0)) 点击控件的左上方 d(text"Settings").click(offset(1, 1)) 点击控件的右下方 拖动控件 d(text"Settings&quo…

LIS路径记录(UVA481)

出自一次很失败的开学测试 LIS自然会做 可以参见&#xff1a;https://blog.csdn.net/Radium_1209/article/details/79704234 由于对于LIS的nlogn算法不熟悉&#xff0c;导致错误理解&#xff0c;记录的路径出现了问题&#xff0c;其中还用了n^2的算法记录路径&#xff08;好理解…

Activemq源码、编译、导入idea、源码调试总结

1、在本地下载源码 在GitHub官网搜activemq&#xff0c;找到排名第一的&#xff0c;并打开&#xff0c;如图所示&#xff0c;拷贝url地址。 activemq托管地址&#xff1a;https://github.com/apache/activemq.git 切换到git bash下&#xff0c;输入命令&#xff1a; mkdir a…

activiti 视图

1. application.properties增加如下配置 spring.activiti.database-schema-updatefalsespring.activiti.db-history-usedfalsespring.activiti.db-identity-usedfalse 2. 视图sql -- 修改表名称 ALTER TABLE act_id_user RENAME act_id_user_bak1; ALTER TABLE act_id_group RE…

ActiveMQ源码解析 建立连接

作为一个消息中间件&#xff0c;有客户端和服务端两部分代码&#xff0c;这次的源码解析系列主要从客户端的代码入手&#xff0c;分成建立连接、消息发送、消息消费三个部分。趁着我昨天弄明白了源码编译的兴奋劲头还没过去&#xff0c;今天研究一下建立连接的部分。 如果读起…

原生Js_实现广告弹窗

广告样式当页面加载后5s刷新在右下角 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>Gary图片轮播</title><style type"text/css">#ad{width:300px;height: 300px;background-color:antiquewhite…

springcloud注册中心eureka

1、前提 springcloud的注册中心是以springboot为基础搭建起来的。 开发工具&#xff1a;IDEA 项目管理工具&#xff1a;maven 2、搭建步骤 创建一个web项目&#xff08;建议使用IDEA工具构建项目&#xff09;修改pom文件 <dependency><groupId>org.springframework…