在spring源码中,在很多地方都会出现PropertyEditor,那么属性编辑器用来干什么的呢?属性编辑器主要应用在以下两个方面:
-
使用PropertyEditors设置Bean属性。当你在XML文件中声明的bean的属性类型为java.lang.String时,Spring将在容器初始化该bean时使用ClassEditor将String解析成Class对象(如果setter方法需要一个Class参数的话)。这么说有点难以理解,请看下面这个代码片段:
<bean class="com.github.thinwonton.spring.source.analysis.propertyeditor.ClassHolder"> <property name="clazz" value="com.github.thinwonton.spring.source.analysis.propertyeditor.Order"/> </bean>public class ClassHolder { private Class<?> clazz;
1public Class<?> getClazz() { 2 return clazz; 3} 4 5public void setClazz(Class<?> clazz) { 6 this.clazz = clazz; 7}}
-
在Spring MVC架构中使用各种PropertyEditors来解析HTTP请求中的参数。
Spring提供了许多内建的PropertyEditors可以简化我们的工作,它们都位于org.springframework.beans.PropertyEditors包内。它们中的大多数已经默认在BeanWrapperImpl的实现类中注册好了。作为可配置的选项,我们也可以注册自己的属性编辑器实现去覆盖那些默认编辑器。
由于PropertyEditor是spring源码分析的基础,于是决定研究一下PropertyEditor。在探索的过程中,发现了下面这篇分析PropertyEditor比较好的文章,就不想再造轮子了。后面还会写一篇关于怎么 在spring中使用自定义PropertyEditor的文章(https://my.oschina.net/thinwonton/blog/1492107),用于补充下面的内容。
=============================================================
在spring配置文件或配置类里,通过字符串为Bean各种属性提供设置值:不管是double类型还是int类型,在配置文件中都对应字符串类型。BeanWrapper填充Bean属性时如何将这个字面值转换为对应的double或int等内部类型呢?我们可以隐约地感觉到一定有一个转换器在其中起作用,这个转换器就是属性编辑器。
1、PropertyEditor
java.beans.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()以及另外一些我们未此介绍的接口方法。
PropertyEditorSupport
Java为PropertyEditor提供了一个方便类:PropertyEditorSupport,该类实现了PropertyEditor接口并提供默认实现,一般情况下,用户可以通过扩展这个方便类设计自己的属性编辑器。
spring中的属性编辑器
Spring大部分默认属性编辑器都直接扩展于java.beans.PropertyEditorSupport类,用户也可以通过扩展PropertyEditorSupport实现自己的属性编辑器。比起用于IDE环境的属性编辑器来说,Spring环境下使用的属性编辑器的功能非常单一:仅需要将配置文件中字面值转换为属性类型的对象即可,并不需要提供UI界面,因此仅需要简单覆盖PropertyEditorSupport的setAsText()方法就可以了。
举个栗子:
实现 UUIDEditor
1public class UUIDEditor extends PropertyEditorSupport { 2 3 @Override 4 public void setAsText(String text) throws IllegalArgumentException { 5 if (StringUtils.hasText(text)) { 6 setValue(UUID.fromString(text)); 7 } 8 else { 9 setValue(null); 10 } 11 } 12 13 @Override 14 public String getAsText() { 15 UUID value = (UUID) getValue(); 16 return (value != null ? value.toString() : ""); 17 } 18 19} 20 21public static void main(String[] args) throws ClassNotFoundException, IOException { 22 23 //UUID对象转化为字符串 24 UUIDEditor editor=new UUIDEditor(); 25 editor.setValue(UUID.randomUUID()); 26 System.out.println(editor.getAsText());//c2878055-fb49-4559-a7db-c60fc3ebee79 27 28 //字符串转化为UUID 29 UUIDEditor editor_2=new UUIDEditor(); 30 editor_2.setAsText("2-1-1-2-3"); 31 System.out.println(editor_2.getAsText());//00000002-0001-0001-0002-000000000003 32 System.out.println(editor_2.getValue().getClass());//class java.util.UUID 33 34}
2、PropertyEditorRegistry
这个接口可以注册和保存属性编辑器
1public interface PropertyEditorRegistry { 2 3 void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor); 4 5 void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor); 6 7 PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath); 8 9}
PropertyEditorRegistrySupport
PropertyEditorRegistrySupport是PropertyEditorRegistry 默认实现类
PropertyEditorRegistrySupport中有两个用于保存属性编辑器的Map类型变量:
1private Map<Class<?>, PropertyEditor> defaultEditors; 2 3private Map<Class<?>, PropertyEditor> customEditors;
PropertyEditorRegistrySupport通过以下的代码定义默认属性编辑器:
1 private void createDefaultEditors() { 2 this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64); 3 4 // Simple editors, without parameterization capabilities. 5 // The JDK does not contain a default editor for any of these target types. 6 this.defaultEditors.put(Charset.class, new CharsetEditor()); 7 this.defaultEditors.put(Class.class, new ClassEditor()); 8 this.defaultEditors.put(Class[].class, new ClassArrayEditor()); 9 this.defaultEditors.put(Currency.class, new CurrencyEditor()); 10 this.defaultEditors.put(File.class, new FileEditor()); 11 this.defaultEditors.put(InputStream.class, new InputStreamEditor()); 12 this.defaultEditors.put(InputSource.class, new InputSourceEditor()); 13 this.defaultEditors.put(Locale.class, new LocaleEditor()); 14 this.defaultEditors.put(Pattern.class, new PatternEditor()); 15 this.defaultEditors.put(Properties.class, new PropertiesEditor()); 16 this.defaultEditors.put(Reader.class, new ReaderEditor()); 17 this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor()); 18 this.defaultEditors.put(TimeZone.class, new TimeZoneEditor()); 19 this.defaultEditors.put(URI.class, new URIEditor()); 20 this.defaultEditors.put(URL.class, new URLEditor()); 21 this.defaultEditors.put(UUID.class, new UUIDEditor()); 22 if (zoneIdClass != null) { 23 this.defaultEditors.put(zoneIdClass, new ZoneIdEditor()); 24 } 25 26 // Default instances of collection editors. 27 // Can be overridden by registering custom instances of those as custom editors. 28 this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class)); 29 this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class)); 30 this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class)); 31 this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class)); 32 this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class)); 33 34 // Default editors for primitive arrays. 35 this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor()); 36 this.defaultEditors.put(char[].class, new CharArrayPropertyEditor()); 37 38 // The JDK does not contain a default editor for char! 39 this.defaultEditors.put(char.class, new CharacterEditor(false)); 40 this.defaultEditors.put(Character.class, new CharacterEditor(true)); 41 42 // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor. 43 this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false)); 44 this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); 45 46 // The JDK does not contain default editors for number wrapper types! 47 // Override JDK primitive number editors with our own CustomNumberEditor. 48 this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false)); 49 this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true)); 50 this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false)); 51 this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true)); 52 this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false)); 53 this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true)); 54 this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false)); 55 this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true)); 56 this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false)); 57 this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true)); 58 this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false)); 59 this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true)); 60 this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true)); 61 this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true)); 62 63 // Only register config value editors if explicitly requested. 64 if (this.configValueEditorsActive) { 65 StringArrayPropertyEditor sae = new StringArrayPropertyEditor(); 66 this.defaultEditors.put(String[].class, sae); 67 this.defaultEditors.put(short[].class, sae); 68 this.defaultEditors.put(int[].class, sae); 69 this.defaultEditors.put(long[].class, sae); 70 } 71 }