You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
752 lines
40 KiB
752 lines
40 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter 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><spring:bind/></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>
|
|
|