5.4.2 Built-in PropertyEditor implementations
Spring uses the concept of PropertyEditors
to effect the conversion between an Object
and a String
. If you think about it, it sometimes might be handy to be able to represent properties in a different way than the object itself. For example, a Date
can be represented in a human readable way (as the String
'2007-14-09'
), while we’re still able to convert the human readable form back to the original date (or even better: convert any date entered in a human readable form, back to Date
objects). This behavior can be achieved by registering custom editors, of type java.beans.PropertyEditor
. Registering custom editors on a BeanWrapper
or alternately in a specific IoC container as mentioned in the previous chapter, gives it the knowledge of how to convert properties to the desired type. Read more aboutPropertyEditors
in the javadocs of the java.beans
package provided by Oracle.
A couple of examples where property editing is used in Spring:
- setting properties on beans is done using
PropertyEditors
. When mentioningjava.lang.String
as the value of a property of some bean you’re declaring in XML file, Spring will (if the setter of the corresponding property has aClass
-parameter) use theClassEditor
to try to resolve the parameter to aClass
object. - parsing HTTP request parameters in Spring’s MVC framework is done using all kinds of
PropertyEditors
that you can manually bind in all subclasses of theCommandController
.
Spring has a number of built-in PropertyEditors
to make life easy. Each of those is listed below and they are all located in the org.springframework.beans.propertyeditors
package. Most, but not all (as indicated below), are registered by default by BeanWrapperImpl
. Where the property editor is configurable in some fashion, you can of course still register your own variant to override the default one:
Table 5.2. Built-in PropertyEditors
Class | Explanation |
---|---|
ByteArrayPropertyEditor |
Editor for byte arrays. Strings will simply be converted to their corresponding byte representations. Registered by default by BeanWrapperImpl . |
ClassEditor |
Parses Strings representing classes to actual classes and the other way around. When a class is not found, an IllegalArgumentException is thrown. Registered by default by BeanWrapperImpl . |
CustomBooleanEditor |
Customizable property editor for Boolean properties. Registered by default by BeanWrapperImpl , but, can be overridden by registering custom instance of it as custom editor. |
CustomCollectionEditor |
Property editor for Collections, converting any source Collection to a given target Collection type. |
CustomDateEditor |
Customizable property editor for java.util.Date, supporting a custom DateFormat. NOT registered by default. Must be user registered as needed with appropriate format. |
CustomNumberEditor |
Customizable property editor for any Number subclass like Integer , Long , Float , Double . Registered by default by BeanWrapperImpl , but can be overridden by registering custom instance of it as a custom editor. |
FileEditor |
Capable of resolving Strings to java.io.File objects. Registered by default by BeanWrapperImpl . |
InputStreamEditor |
One-way property editor, capable of taking a text string and producing (via an intermediate ResourceEditor and Resource ) an InputStream , so InputStream properties may be directly set as Strings. Note that the default usage will not close the InputStream for you! Registered by default by BeanWrapperImpl . |
LocaleEditor |
Capable of resolving Strings to Locale objects and vice versa (the String format is [country][variant], which is the same thing the toString() method of Locale provides). Registered by default by BeanWrapperImpl . |
PatternEditor |
Capable of resolving Strings to java.util.regex.Pattern objects and vice versa. |
PropertiesEditor |
Capable of converting Strings (formatted using the format as defined in the javadocs of the java.util.Properties class) to Properties objects. Registered by default by BeanWrapperImpl . |
StringTrimmerEditor |
Property editor that trims Strings. Optionally allows transforming an empty string into a null value. NOT registered by default; must be user registered as needed. |
URLEditor |
Capable of resolving a String representation of a URL to an actual URL object. Registered by default by BeanWrapperImpl . |
Spring uses the java.beans.PropertyEditorManager
to set the search path for property editors that might be needed. The search path also includes sun.bean.editors
, which includes PropertyEditor
implementations for types such as Font
, Color
, and most of the primitive types. Note also that the standard JavaBeans infrastructure will automatically discover PropertyEditor
classes (without you having to register them explicitly) if they are in the same package as the class they handle, and have the same name as that class, with 'Editor'
appended; for example, one could have the following class and package structure, which would be sufficient for the FooEditor
class to be recognized and used as the PropertyEditor
for Foo
-typed properties.
com
chank
pop
Foo
FooEditor // the PropertyEditor for the Foo class
Note that you can also use the standard BeanInfo
JavaBeans mechanism here as well (described in not-amazing-detail here). Find below an example of using the BeanInfo
mechanism for explicitly registering one or more PropertyEditor
instances with the properties of an associated class.
com
chank
pop
Foo
FooBeanInfo // the BeanInfo for the Foo class
Here is the Java source code for the referenced FooBeanInfo
class. This would associate a CustomNumberEditor
with the age
property of the Foo
class.
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());
}
}
}
Registering additional custom PropertyEditors
When setting bean properties as a string value, a Spring IoC container ultimately uses standard JavaBeans PropertyEditors
to convert these Strings to the complex type of the property. Spring pre-registers a number of custom PropertyEditors
(for example, to convert a classname expressed as a string into a real Class
object). Additionally, Java’s standard JavaBeans PropertyEditor
lookup mechanism allows a PropertyEditor
for a class simply to be named appropriately and placed in the same package as the class it provides support for, to be found automatically.
If there is a need to register other custom PropertyEditors
, there are several mechanisms available. The most manual approach, which is not normally convenient or recommended, is to simply use the registerCustomEditor()
method of the ConfigurableBeanFactory
interface, assuming you have a BeanFactory
reference. Another, slightly more convenient, mechanism is to use a special bean factory post-processor called CustomEditorConfigurer
. Although bean factory post-processors can be used with BeanFactory
implementations, the CustomEditorConfigurer
has a nested property setup, so it is strongly recommended that it is used with theApplicationContext
, where it may be deployed in similar fashion to any other bean, and automatically detected and applied.
Note that all bean factories and application contexts automatically use a number of built-in property editors, through their use of something called a BeanWrapper
to handle property conversions. The standard property editors that the BeanWrapper
registers are listed in the previous section. Additionally, ApplicationContexts
also override or add an additional number of editors to handle resource lookups in a manner appropriate to the specific application context type.
Standard JavaBeans PropertyEditor
instances are used to convert property values expressed as strings to the actual complex type of the property.CustomEditorConfigurer
, a bean factory post-processor, may be used to conveniently add support for additional PropertyEditor
instances to an ApplicationContext
.
Consider a user class ExoticType
, and another class DependsOnExoticType
which needs ExoticType
set as a property:
package example;
public class ExoticType {
private String name;
public ExoticType(String name) {
this.name = name;
}
}
public class DependsOnExoticType {
private ExoticType type;
public void setType(ExoticType type) {
this.type = type;
}
}
When things are properly set up, we want to be able to assign the type property as a string, which a PropertyEditor
will behind the scenes convert into an actualExoticType
instance:
<bean id="sample" class="example.DependsOnExoticType">
<property name="type" value="aNameForExoticType"/>
</bean>
The PropertyEditor
implementation could look similar to this:
// converts string representation to ExoticType object
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
public void setAsText(String text) {
setValue(new ExoticType(text.toUpperCase()));
}
}
Finally, we use CustomEditorConfigurer
to register the new PropertyEditor
with the ApplicationContext
, which will then be able to use it as needed:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="example.ExoticType" value="example.ExoticTypeEditor"/>
</map>
</property>
</bean>
Using PropertyEditorRegistrars
Another mechanism for registering property editors with the Spring container is to create and use a PropertyEditorRegistrar
. This interface is particularly useful when you need to use the same set of property editors in several different situations: write a corresponding registrar and reuse that in each case. PropertyEditorRegistrars
work in conjunction with an interface called PropertyEditorRegistry
, an interface that is implemented by the Spring BeanWrapper
(and DataBinder
). PropertyEditorRegistrars
are particularly convenient when used in conjunction with the CustomEditorConfigurer
(introduced here), which exposes a property called setPropertyEditorRegistrars(..)
: PropertyEditorRegistrars
added to a CustomEditorConfigurer
in this fashion can easily be shared with DataBinder
and Spring MVC Controllers
. Furthermore, it avoids the need for synchronization on custom editors: a PropertyEditorRegistrar
is expected to create fresh PropertyEditor
instances for each bean creation attempt.
Using a PropertyEditorRegistrar
is perhaps best illustrated with an example. First off, you need to create your own PropertyEditorRegistrar
implementation:
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...
}
}
See also the org.springframework.beans.support.ResourceEditorRegistrar
for an example PropertyEditorRegistrar
implementation. Notice how in its implementation of the registerCustomEditors(..)
method it creates new instances of each property editor.
Next we configure a CustomEditorConfigurer
and inject an instance of our CustomPropertyEditorRegistrar
into it:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="customPropertyEditorRegistrar"/>
</list>
</property>
</bean>
<bean id="customPropertyEditorRegistrar"
class="com.foo.editors.spring.CustomPropertyEditorRegistrar"/>
Finally, and in a bit of a departure from the focus of this chapter, for those of you using Spring’s MVC web framework, using PropertyEditorRegistrars
in conjunction with data-binding Controllers
(such as SimpleFormController
) can be very convenient. Find below an example of using a PropertyEditorRegistrar
in the implementation of an initBinder(..)
method:
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
}
This style of PropertyEditor
registration can lead to concise code (the implementation of initBinder(..)
is just one line long!), and allows common PropertyEditor
registration code to be encapsulated in a class and then shared amongst as many Controllers
as needed.