spring-framework/spring-framework-reference/src/validation.xml

753 lines
40 KiB
XML
Raw Normal View History

<?xml version="1.0" encoding="UTF-8"?>
2009-04-15 05:37:40 +08:00
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="validation">
<title>Validation, Data-binding, the <interfacename>BeanWrapper</interfacename>, and <literal>PropertyEditors</literal></title>
<section id="validation-introduction">
<title>Introduction</title>
<para>There are pros and cons for considering validation as business logic,
and Spring offers a design for validation (and data binding) that
does not exclude either one of them. Specifically validation should not be
tied to the web tier, should be easy to localize and it should be
possible to plug in any validator available. Considering the above, Spring
has come up with a <interfacename>Validator</interfacename> interface that
is both basic and eminently usable in every layer of an application.</para>
<para>Data binding is useful for allowing user input to be dynamically
bound to the domain model of an application (or whatever objects you use
to process user input). Spring provides the so-called
<interfacename>DataBinder</interfacename> to do exactly that. The
<interfacename>Validator</interfacename> and the
<interfacename>DataBinder</interfacename> make up the <literal>validation</literal> package,
which is primarily used in but not limited to the MVC framework.</para>
<para>The <interfacename>BeanWrapper</interfacename> is a fundamental concept in the
Spring Framework and is used in a lot of places. However, you probably
will not ever have the need to use the <interfacename>BeanWrapper</interfacename> directly. Because this
is reference documentation however, we felt that some explanation might be
in order. We're explaining the <interfacename>BeanWrapper</interfacename> in this chapter since if you were
going to use it at all, you would probably do so when trying to bind
data to objects, which is strongly related to the <interfacename>BeanWrapper</interfacename>.</para>
<para>Spring uses PropertyEditors all over the place. The concept of a
<interfacename>PropertyEditor</interfacename> is part of the JavaBeans specification. Just as the
<interfacename>BeanWrapper</interfacename>, it's best to explain the use of PropertyEditors in this
chapter as well, since it's closely related to the <interfacename>BeanWrapper</interfacename> and the
<interfacename>DataBinder</interfacename>.</para>
</section>
<section id="validator">
<title>Validation using Spring's <interfacename>Validator</interfacename> interface</title>
<para>Spring's features a <interfacename>Validator</interfacename> interface that you can
use to validate objects. The <interfacename>Validator</interfacename> interface works using
an <interfacename>Errors</interfacename> object so that while validating, validators can report
validation failures to the <interfacename>Errors</interfacename> object.</para>
<para>Let's consider a small data object:</para>
<programlisting language="java"><![CDATA[
public class Person {
private String name;
private int age;
]]><lineannotation>// the usual getters and setters...</lineannotation><![CDATA[
}]]></programlisting>
<para>We're going to provide validation behavior for the <classname>Person</classname>
class by implementing the following two methods of the
<interfacename>org.springframework.validation.Validator</interfacename> interface:
<itemizedlist spacing="compact">
<listitem>
<para><methodname>supports(Class)</methodname> - Can this
<interfacename>Validator</interfacename> validate instances of the supplied
<classname>Class</classname>?</para>
</listitem>
<listitem>
<para><methodname>validate(Object, org.springframework.validation.Errors)</methodname> -
validates the given object and in case of validation errors, registers
those with the given <interfacename>Errors</interfacename> object</para>
</listitem>
</itemizedlist>
</para>
<para>
Implementing a <interfacename>Validator</interfacename> is fairly straightforward,
especially when you know of the <classname>ValidationUtils</classname> helper class
that the Spring Framework also provides.</para>
<programlisting language="java"><![CDATA[public class PersonValidator implements Validator {
]]><lineannotation>/**
* This <interfacename>Validator</interfacename> validates <emphasis role="bold">just</emphasis> <classname>Person</classname> instances
*/</lineannotation><![CDATA[
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}]]></programlisting>
<para>As you can see, the <literal>static</literal> <methodname>rejectIfEmpty(..)</methodname>
method on the <classname>ValidationUtils</classname> class is used to reject the
<literal>'name'</literal> property if it is <literal>null</literal> or the empty string.
Have a look at the Javadoc for the <classname>ValidationUtils</classname> class to see
what functionality it provides besides the example shown previously.</para>
<para>While it is certainly possible to implement a single
<interfacename>Validator</interfacename> class to validate each of the nested objects
in a rich object, it may be better to encapsulate the validation logic for each nested
class of object in its own <interfacename>Validator</interfacename> implementation. A
simple example of a <emphasis>'rich'</emphasis> object would be a
<classname>Customer</classname> that is composed of two <classname>String</classname>
properties (a first and second name) and a complex <classname>Address</classname> object.
<classname>Address</classname> objects may be used independently of
<classname>Customer</classname> objects, and so a distinct
<classname>AddressValidator</classname> has been implemented. If you want your
<classname>CustomerValidator</classname> to reuse the logic contained within the
<classname>AddressValidator</classname> class without recourse to copy-n-paste you can
dependency-inject or instantiate an <classname>AddressValidator</classname> within your
<classname>CustomerValidator</classname>, and use it like so:</para>
<programlisting language="java"><![CDATA[public class CustomerValidator implements Validator {
private final Validator addressValidator;
public CustomerValidator(Validator addressValidator) {
if (addressValidator == null) {
throw new IllegalArgumentException("The supplied [Validator] is required and must not be null.");
}
if (!addressValidator.supports(Address.class)) {
throw new IllegalArgumentException(
"The supplied [Validator] must support the validation of [Address] instances.");
}
this.addressValidator = addressValidator;
}
]]><lineannotation>/**
* This <interfacename>Validator</interfacename> validates <classname>Customer</classname> instances, and any subclasses of <classname>Customer</classname> too
*/</lineannotation><![CDATA[
public boolean supports(Class clazz) {
return Customer.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
Customer customer = (Customer) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
} finally {
errors.popNestedPath();
}
}
}]]></programlisting>
<para>Validation errors are reported to the <interfacename>Errors</interfacename>
object passed to the validator. In case of Spring Web MVC you can use
<literal>&lt;spring:bind/&gt;</literal> tag to inspect the error messages, but
of course you can also inspect the errors object yourself. More information about
the methods it offers can be found from the Javadoc.</para>
</section>
<section id="validation-conversion">
<title>Resolving codes to error messages</title>
<para>We've talked about databinding and validation. Outputting messages corresponding to
validation errors is the last thing we need to discuss. In the example we've shown
above, we rejected the <literal>name</literal> and the <literal>age</literal> field.
If we're going to output the error messages by using a <interfacename>MessageSource</interfacename>,
we will do so using the error code we've given when rejecting the field ('name' and 'age'
in this case). When you call (either directly, or indirectly, using for example the
<classname>ValidationUtils</classname> class) <literal>rejectValue</literal> or one of
the other <literal>reject</literal> methods from the <interfacename>Errors</interfacename>
interface, the underlying implementation will not only register the code you've
passed in, but also a number of additional error codes. What error codes it registers
is determined by the <interfacename>MessageCodesResolver</interfacename> that is used.
By default, the <classname>DefaultMessageCodesResolver</classname> is used, which for example
not only registers a message with the code you gave, but also messages that include the
field name you passed to the reject method. So in case you reject a field using
<literal>rejectValue("age", "too.darn.old")</literal>, apart from the
<literal>too.darn.old</literal> code, Spring will also register
<literal>too.darn.old.age</literal> and <literal>too.darn.old.age.int</literal>
(so the first will include the field name and the second will include the type of the
field); this is done as a convenience to aid developers in targeting error
messages and suchlike.</para>
<para>More information on the <interfacename>MessageCodesResolver</interfacename> and the default
strategy can be found online with the Javadocs for
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/validation/MessageCodesResolver.html">MessageCodesResolver</ulink>
and
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/validation/DefaultMessageCodesResolver.html">DefaultMessageCodesResolver</ulink>
respectively.</para>
</section>
<section id="beans-beans">
<title>Bean manipulation and the <interfacename>BeanWrapper</interfacename></title>
<para>The <literal>org.springframework.beans</literal> package adheres to
the JavaBeans standard provided by Sun. A JavaBean is simply a class with
a default no-argument constructor, which follows a naming convention
where (by way of an example) a property named <literal>bingoMadness</literal> would have a setter
method <methodname>setBingoMadness(..)</methodname> and a getter method <methodname>getBingoMadness()</methodname>.
For more information about JavaBeans and the specification, please refer
to Sun's website ( <ulink url="http://java.sun.com/products/javabeans/">java.sun.com/products/javabeans</ulink>).</para>
<para>One quite important class in the beans package is the
<interfacename>BeanWrapper</interfacename> interface and its corresponding
implementation (<classname>BeanWrapperImpl</classname>). As quoted from the
Javadoc, the <interfacename>BeanWrapper</interfacename> offers functionality to set and get property
values (individually or in bulk), get property descriptors, and to query
properties to determine if they are readable or writable. Also, the
<interfacename>BeanWrapper</interfacename> offers support for nested properties, enabling the setting of
properties on sub-properties to an unlimited depth. Then, the <interfacename>BeanWrapper</interfacename>
supports the ability to add standard JavaBeans
<interfacename>PropertyChangeListeners</interfacename> and
<interfacename>VetoableChangeListeners</interfacename>, without the need for
supporting code in the target class. Last but not least, the <interfacename>BeanWrapper</interfacename>
provides support for the setting of indexed properties. The <interfacename>BeanWrapper</interfacename>
usually isn't used by application code directly, but by the
<interfacename>DataBinder</interfacename> and the
<interfacename>BeanFactory</interfacename>.</para>
<para>The way the <interfacename>BeanWrapper</interfacename> works is partly indicated by its name:
<emphasis>it wraps a bean</emphasis> to perform actions on that bean, like
setting and retrieving properties.</para>
<section id="beans-beans-conventions">
<title>Setting and getting basic and nested properties</title>
<para>Setting and getting properties is done using the
<literal>setPropertyValue(s)</literal> and
<literal>getPropertyValue(s)</literal> methods that both come with a
couple of overloaded variants. They're all described in more detail in
the Javadoc Spring comes with. What's important to know is that there
are a couple of conventions for indicating properties of an object. A
couple of examples:</para>
<table id="beans-beans-conventions-properties-tbl">
<title>Examples of properties</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="1*" />
<colspec colname="c2" colwidth="3*" />
<thead>
<row>
<entry>Expression</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>name</literal></entry>
<entry>Indicates the property <literal>name</literal>
corresponding to the methods <methodname>getName()</methodname> or
<methodname>isName()</methodname> and
<methodname>setName(..)</methodname></entry>
</row>
<row>
<entry><literal>account.name</literal></entry>
<entry>Indicates the nested property <literal>name</literal>
of the property <literal>account</literal> corresponding e.g.
to the methods <literal>getAccount().setName()</literal> or
<literal>getAccount().getName()</literal></entry>
</row>
<row>
<entry><literal>account[2]</literal></entry>
<entry>Indicates the <emphasis>third</emphasis> element of the
indexed property <literal>account</literal>. Indexed
properties can be of type <literal>array</literal>,
<literal>list</literal> or other <emphasis>naturally
ordered</emphasis> collection</entry>
</row>
<row>
<entry><literal>account[COMPANYNAME]</literal></entry>
<entry>Indicates the value of the map entry indexed by the key
<emphasis>COMPANYNAME</emphasis> of the Map property
<literal>account</literal></entry>
</row>
</tbody>
</tgroup>
</table>
<para>Below you'll find some examples of working with the <interfacename>BeanWrapper</interfacename> to
get and set properties.</para>
<para><emphasis>(This next section is not vitally important to you if you're not
planning to work with the <interfacename>BeanWrapper</interfacename> directly. If you're
just using the <interfacename>DataBinder</interfacename> and the
<interfacename>BeanFactory</interfacename> and their out-of-the-box implementation, you
should skip ahead to the section about
<interfacename>PropertyEditors</interfacename>.)</emphasis></para>
<para>Consider the following two classes:</para>
<programlisting language="java"><![CDATA[public class Company {
private String name;
private Employee managingDirector;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Employee getManagingDirector() {
return this.managingDirector;
}
public void setManagingDirector(Employee managingDirector) {
this.managingDirector = managingDirector;
}
}]]></programlisting>
<programlisting language="java"><![CDATA[public class Employee {
private String name;
private float salary;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public float getSalary() {
return salary;
}
public void setSalary(float salary) {
this.salary = salary;
}
}]]></programlisting>
<para>The following code snippets show some examples of how to retrieve
and manipulate some of the properties of instantiated
<literal>Companies</literal> and <literal>Employees</literal>:</para>
<programlisting language="java"><![CDATA[BeanWrapper company = BeanWrapperImpl(new Company());
]]><lineannotation>// setting the company name..</lineannotation><![CDATA[
company.setPropertyValue("name", "Some Company Inc.");
]]><lineannotation>// ... can also be done like this:</lineannotation><![CDATA[
PropertyValue value = new PropertyValue("name", "Some Company Inc.");
company.setPropertyValue(value);
]]><lineannotation>// ok, let's create the director and tie it to the company:</lineannotation><![CDATA[
BeanWrapper jim = BeanWrapperImpl(new Employee());
jim.setPropertyValue("name", "Jim Stravinsky");
company.setPropertyValue("managingDirector", jim.getWrappedInstance());
]]><lineannotation>// retrieving the salary of the managingDirector through the company</lineannotation><![CDATA[
Float salary = (Float) company.getPropertyValue("managingDirector.salary");]]></programlisting>
</section>
<section id="beans-beans-conversion">
<title>Built-in <interface>PropertyEditor</interface> implementations</title>
<para>Spring heavily uses the concept of <literal>PropertyEditors</literal> to effect the conversion
between an <classname>Object</classname> and a <classname>String</classname>. 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 <classname>Date</classname> can be represented in a human readable way (as the
<classname>String</classname> '<literal>2007-14-09</literal>'), 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 <classname>Date</classname> objects). This behavior can be achieved by
<emphasis>registering custom editors</emphasis>, of type <interfacename>java.beans.PropertyEditor</interfacename>.
Registering custom editors on a <interfacename>BeanWrapper</interfacename> 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 about <interfacename>PropertyEditors</interfacename> in the Javadoc of the
<literal>java.beans</literal> package provided by Sun.</para>
<para>A couple of examples where property editing is used in Spring:
<itemizedlist spacing="compact">
<listitem>
<para><emphasis>setting properties on beans</emphasis> is done
using <literal>PropertyEditors</literal>. When mentioning
<literal>java.lang.String</literal> 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 a <classname>Class</classname>-parameter) use the
<classname>ClassEditor</classname> to try to resolve the parameter to
a <classname>Class</classname> object.</para>
</listitem>
<listitem>
<para><emphasis>parsing HTTP request parameters</emphasis> in
Spring's MVC framework is done using all kinds of <literal>PropertyEditors</literal>
that you can manually bind in all subclasses of the
<classname>CommandController</classname>.</para>
</listitem>
</itemizedlist>
</para>
<para>Spring has a number of built-in <literal>PropertyEditors</literal> to make life easy.
Each of those is listed below and they are all located in the
<literal>org.springframework.beans.propertyeditors</literal> package. Most, but not all (as indicated below),
are registered by default by <classname>BeanWrapperImpl</classname>. Where the property editor is configurable
in some fashion, you can of course still register your own variant to override the default one:</para>
<table id="beans-beans-property-editors-tbl">
<title>Built-in <literal>PropertyEditors</literal></title>
<tgroup cols="2">
<colspec colname="c1" colwidth="3*" />
<colspec colname="c2" colwidth="5*" />
<thead>
<row>
<entry>Class</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry><classname>ByteArrayPropertyEditor</classname></entry>
<entry>Editor for byte arrays. Strings will simply be
converted to their corresponding byte representations.
Registered by default by <classname>BeanWrapperImpl</classname>.</entry>
</row>
<row>
<entry><classname>ClassEditor</classname></entry>
<entry>Parses Strings representing classes to actual classes
and the other way around. When a class is not found, an
<classname>IllegalArgumentException</classname> is thrown. Registered by default by
<classname>BeanWrapperImpl</classname>.</entry>
</row>
<row>
<entry><classname>CustomBooleanEditor</classname></entry>
<entry>Customizable property editor for <classname>Boolean</classname> properties.
Registered by default by <classname>BeanWrapperImpl</classname>, but, can be
overridden by registering custom instance of it as custom
editor.</entry>
</row>
<row>
<entry><classname>CustomCollectionEditor</classname></entry>
<entry>Property editor for Collections, converting any source
<interfacename>Collection</interfacename> to a given target <interfacename>Collection</interfacename> type.</entry>
</row>
<row>
<entry><classname>CustomDateEditor</classname></entry>
<entry>Customizable property editor for java.util.Date,
supporting a custom DateFormat. NOT registered by default. Must
be user registered as needed with appropriate format.</entry>
</row>
<row>
<entry><classname>CustomNumberEditor</classname></entry>
<entry>Customizable property editor for any Number subclass
like <classname>Integer</classname>, <classname>Long</classname>,
<classname>Float</classname>, <classname>Double</classname>. Registered
by default by <classname>BeanWrapperImpl</classname>, but can be
overridden by registering custom instance of it as a custom editor.</entry>
</row>
<row>
<entry><classname>FileEditor</classname></entry>
<entry>Capable of resolving Strings to
<classname>java.io.File</classname> objects. Registered by default by
<classname>BeanWrapperImpl</classname>. </entry>
</row>
<row>
<entry><classname>InputStreamEditor</classname></entry>
<entry>One-way property editor, capable of taking a text
string and producing (via an intermediate <classname>ResourceEditor</classname> and
<interfacename>Resource</interfacename>) an
<interfacename>InputStream</interfacename>, so <interfacename>InputStream</interfacename>
properties may be directly set as Strings. Note that the default usage
will not close the <interfacename>InputStream</interfacename> for
you! Registered by default by <classname>BeanWrapperImpl</classname>.</entry>
</row>
<row>
<entry><classname>LocaleEditor</classname></entry>
<entry>Capable of resolving Strings to
<classname>Locale</classname> objects and vice versa (the String
format is [language]_[country]_[variant], which is the same
thing the toString() method of Locale provides). Registered by
default by <classname>BeanWrapperImpl</classname>.</entry>
</row>
<row>
<entry><classname>PatternEditor</classname></entry>
<entry>Capable of resolving Strings to JDK 1.5
<classname>Pattern</classname> objects and vice versa.</entry>
</row>
<row>
<entry><classname>PropertiesEditor</classname></entry>
<entry>Capable of converting Strings (formatted using the
format as defined in the Javadoc for the java.lang.Properties
class) to <classname>Properties</classname> objects. Registered by
default by <classname>BeanWrapperImpl</classname>.</entry>
</row>
<row>
<entry><classname>StringTrimmerEditor</classname></entry>
<entry>Property editor that trims Strings. Optionally allows
transforming an empty string into a <literal>null</literal> value. NOT
registered by default; must be user registered as needed.</entry>
</row>
<row>
<entry><classname>URLEditor</classname></entry>
<entry>Capable of resolving a String representation of a URL
to an actual <classname>URL</classname> object. Registered by
default by <classname>BeanWrapperImpl</classname>.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
Spring uses the <interfacename>java.beans.PropertyEditorManager</interfacename> to set
the search path for property editors that might be needed. The search path also includes
<literal>sun.bean.editors</literal>, which includes
<interfacename>PropertyEditor</interfacename> implementations for types such as
<classname>Font</classname>, <classname>Color</classname>, and most of the primitive types.
Note also that the standard JavaBeans infrastructure will automatically discover
<interfacename>PropertyEditor</interfacename> 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 <literal>'Editor'</literal> appended; for example, one could have the
following class and package structure, which would be sufficient for the
<classname>FooEditor</classname> class to be recognized and used as the
<interfacename>PropertyEditor</interfacename> for <classname>Foo</classname>-typed
properties.
</para>
<programlisting><![CDATA[com
chank
pop
Foo
FooEditor ]]><lineannotation>// the <interfacename>PropertyEditor</interfacename> for the <classname>Foo</classname> class</lineannotation></programlisting>
<para>Note that you can also use the standard <interfacename>BeanInfo</interfacename> JavaBeans
mechanism here as well (described
<ulink url="http://java.sun.com/docs/books/tutorial/javabeans/customization/index.html">in not-amazing-detail here</ulink>).
Find below an example of using the <interfacename>BeanInfo</interfacename> mechanism for
explicitly registering one or more <interfacename>PropertyEditor</interfacename> instances
with the properties of an associated class.</para>
<programlisting><![CDATA[com
chank
pop
Foo
FooBeanInfo ]]><lineannotation>// the <interfacename>BeanInfo</interfacename> for the <classname>Foo</classname> class</lineannotation></programlisting>
<para>
Here is the Java source code for the referenced <classname>FooBeanInfo</classname> class. This
would associate a <classname>CustomNumberEditor</classname> with the <literal>age</literal>
property of the <classname>Foo</classname> class.
</para>
<programlisting language="java"><![CDATA[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());
}
}
}]]></programlisting>
<section id="beans-beans-conversion-customeditor-registration">
<title>Registering additional custom <interfacename>PropertyEditors</interfacename></title>
<para>When setting bean properties as a string value, a Spring IoC container
ultimately uses standard JavaBeans <literal>PropertyEditors</literal> to convert these
Strings to the complex type of the property. Spring pre-registers a number
of custom <literal>PropertyEditors</literal> (for example, to convert a classname expressed
as a string into a real <classname>Class</classname> object). Additionally, Java's standard
JavaBeans <interfacename>PropertyEditor</interfacename> lookup mechanism allows a
<classname>PropertyEditor</classname> 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.</para>
<para>If there is a need to register other custom <literal>PropertyEditors</literal>, there
are several mechanisms available. The most manual approach, which is not normally convenient or
recommended, is to simply use the <methodname>registerCustomEditor()</methodname> method of the
<interfacename>ConfigurableBeanFactory</interfacename> interface, assuming you have a
<interfacename>BeanFactory</interfacename> reference. Another, slightly more convenient, mechanism is to use
a special bean factory post-processor called <classname>CustomEditorConfigurer</classname>.
Although bean factory post-processors can be used with <interfacename>BeanFactory</interfacename>
implementations, the <classname>CustomEditorConfigurer</classname> has a nested property setup, so it is
strongly recommended that it is used with the <interfacename>ApplicationContext</interfacename>, where
it may be deployed in similar fashion to any other bean, and automatically detected and applied.</para>
<para>Note that all bean factories and application contexts automatically use a number of built-in property
editors, through their use of something called a <interfacename>BeanWrapper</interfacename> to handle
property conversions. The standard property editors that the <interfacename>BeanWrapper</interfacename>
registers are listed in <link linkend="beans-beans-conversion">the previous section</link>. Additionally,
<literal>ApplicationContexts</literal> also override or add an additional number of editors
to handle resource lookups in a manner appropriate to the specific application context type.</para>
<para>Standard JavaBeans <interfacename>PropertyEditor</interfacename> instances are used to convert
property values expressed as strings to the actual complex type of the property.
<classname>CustomEditorConfigurer</classname>, a bean factory post-processor, may be used to conveniently
add support for additional <interfacename>PropertyEditor</interfacename> instances to an
<interfacename>ApplicationContext</interfacename>.</para>
<para>Consider a user class <classname>ExoticType</classname>, and another class
<classname>DependsOnExoticType</classname> which needs <classname>ExoticType</classname> set as a property:</para>
<programlisting language="java"><![CDATA[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;
}
}]]></programlisting>
<para>When things are properly set up, we want to be able to assign the type property as a string, which a
<interfacename>PropertyEditor</interfacename> will behind the scenes convert into an actual
<classname>ExoticType</classname> instance:</para>
<programlisting language="xml"><![CDATA[<bean id="sample" class="example.DependsOnExoticType">
<property name="type" value="aNameForExoticType"/>
</bean>]]></programlisting>
<para>The <interfacename>PropertyEditor</interfacename> implementation could look similar to this:</para>
<programlisting language="java"><lineannotation>// converts string representation to <classname>ExoticType</classname> object</lineannotation><![CDATA[
package example;
public class ExoticTypeEditor extends PropertyEditorSupport {
private String format;
public void setFormat(String format) {
this.format = format;
}
public void setAsText(String text) {
if (format != null && format.equals("upperCase")) {
text = text.toUpperCase();
}
ExoticType type = new ExoticType(text);
setValue(type);
}
}]]></programlisting>
<para>Finally, we use <classname>CustomEditorConfigurer</classname> to register the new
<interfacename>PropertyEditor</interfacename> with the <interfacename>ApplicationContext</interfacename>,
which will then be able to use it as needed:</para>
<programlisting language="xml"><![CDATA[<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="example.ExoticType">
<bean class="example.ExoticTypeEditor">
<property name="format" value="upperCase"/>
</bean>
</entry>
</map>
</property>
</bean>]]></programlisting>
<section id="beans-beans-conversion-customeditor-registration-per">
<title>Using <interfacename>PropertyEditorRegistrars</interfacename></title>
<para>Another mechanism for registering property editors with the Spring container is to create and use
a <interfacename>PropertyEditorRegistrar</interfacename>. 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. <literal>PropertyEditorRegistrars</literal> work in conjunction
with an interface called <interfacename>PropertyEditorRegistry</interfacename>, an interface
that is implemented by the Spring <interfacename>BeanWrapper</interfacename> (and
<interfacename>DataBinder</interfacename>). <literal>PropertyEditorRegistrars</literal> are particularly
convenient when used in conjunction with the <classname>CustomEditorConfigurer</classname>
(introduced <link linkend="beans-beans-conversion-customeditor-registration">here</link>), which exposes a
property called <methodname>setPropertyEditorRegistrars(..)</methodname>:
<literal>PropertyEditorRegistrars</literal> added to a <classname>CustomEditorConfigurer</classname> in this
fashion can easily be shared with <interfacename>DataBinder</interfacename> and Spring MVC
<interfacename>Controllers</interfacename>. Furthermore, it avoids the need for synchronization on custom
editors: a <interfacename>PropertyEditorRegistrar</interfacename> is expected to create fresh
<interfacename>PropertyEditor</interfacename> instances for each bean creation attempt.</para>
<para>Using a <interfacename>PropertyEditorRegistrar</interfacename> is perhaps best illustrated with an
example. First off, you need to create your own <interfacename>PropertyEditorRegistrar</interfacename>
implementation:</para>
<programlisting language="java"><![CDATA[package com.foo.editors.spring;
public final class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
]]><lineannotation>// it is expected that new <interfacename>PropertyEditor</interfacename> instances are created</lineannotation><![CDATA[
registry.registerCustomEditor(ExoticType.class, new ExoticTypeEditor());
]]><lineannotation>// you could register as many custom property editors as are required here...</lineannotation><![CDATA[
}
}]]></programlisting>
<para>See also the <classname>org.springframework.beans.support.ResourceEditorRegistrar</classname> for an
example <interfacename>PropertyEditorRegistrar</interfacename> implementation. Notice how in its
implementation of the <methodname>registerCustomEditors(..)</methodname> method it creates new instances
of each property editor.</para>
<para>Next we configure a <classname>CustomEditorConfigurer</classname> and inject an
instance of our <classname>CustomPropertyEditorRegistrar</classname> into it:</para>
<programlisting language="xml"><![CDATA[<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"/>]]></programlisting>
<para>Finally, and in a bit of a departure from the focus of this chapter, for those of you using
<link linkend="mvc">Spring's MVC web framework</link>, using <interfacename>PropertyEditorRegistrars</interfacename>
in conjunction with data-binding <interfacename>Controllers</interfacename> (such as
<classname>SimpleFormController</classname>) can be very convenient. Find below an example of using a
<interfacename>PropertyEditorRegistrar</interfacename> in the implementation of an <methodname>initBinder(..)</methodname>
method:</para>
<programlisting language="java"><![CDATA[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 {
]]><emphasis role="bold">this.customPropertyEditorRegistrar.registerCustomEditors(binder);</emphasis><![CDATA[
}
]]><lineannotation>// other methods to do with registering a <classname>User</classname></lineannotation><![CDATA[
}]]></programlisting>
<para>This style of <interfacename>PropertyEditor</interfacename> registration can lead to concise code (the
implementation of <methodname>initBinder(..)</methodname> is just one line long!), and allows common
<interfacename>PropertyEditor</interfacename> registration code to be encapsulated in a class and then
shared amongst as many <interfacename>Controllers</interfacename> as needed.</para>
</section>
</section>
</section>
</section>
</chapter>