polishing

This commit is contained in:
Sam Brannen 2009-10-06 21:11:51 +00:00
parent b7e36461ca
commit 2c2d79a4bf
1 changed files with 62 additions and 87 deletions

View File

@ -752,7 +752,7 @@ public final class CustomPropertyEditorRegistrar implements PropertyEditorRegist
<title>Spring 3 Type Conversion</title> <title>Spring 3 Type Conversion</title>
<para> <para>
Spring 3 introduces a <filename>core.convert</filename> package that provides a general type conversion system. Spring 3 introduces a <filename>core.convert</filename> package that provides a general type conversion system.
The system defines a SPI to implement type conversion logic, as well as a API to execute type conversions at runtime. The system defines an SPI to implement type conversion logic, as well as an API to execute type conversions at runtime.
Within a Spring container, if configured, this system can be used as an alternative to PropertyEditors to convert externalized bean property value strings to required property types. Within a Spring container, if configured, this system can be used as an alternative to PropertyEditors to convert externalized bean property value strings to required property types.
The public API may also be used anywhere in your application where type conversion is needed. The public API may also be used anywhere in your application where type conversion is needed.
</para> </para>
@ -768,53 +768,49 @@ public interface Converter<S, T> {
T convert(S source) throws Exception; T convert(S source) throws Exception;
}]]> }]]></programlisting>
</programlisting>
<para> <para>
To create your own Converter, simply implement the interface above. To create your own Converter, simply implement the interface above.
Parameterize S as the type you are converting from, and T as the type you are converting to. Parameterize S as the type you are converting from, and T as the type you are converting to.
For each call to convert(S), the source argument is guaranteed to be NOT null. For each call to convert(S), the source argument is guaranteed to be NOT null.
Your Converter may throw any Exception if conversion fails. Your Converter may throw any Exception if conversion fails.
An IllegalArgumentException is often thrown to report an invalid source value. An IllegalArgumentException is often thrown to report an invalid source value.
Take care to ensure your Converter implementation is thread safe. Take care to ensure your Converter implementation is thread-safe.
</para> </para>
<para> <para>
Several converter implementations are provided in the <filename>core.convert.converters</filename> package as a convenience. Several converter implementations are provided in the <filename>core.convert.converters</filename> package as a convenience.
These include converters to from String to Numbers and other common types. These include converters from Strings to Numbers and other common types.
Note StringToInteger as an example Converter implementation: Consider <classname>StringToInteger</classname> as an example Converter implementation:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java">package org.springframework.core.convert.converters;
package org.springframework.core.convert.converters;
public class StringToInteger implements Converter<String, Integer> { public class StringToInteger implements Converter&lt;String, Integer&gt; {
public Integer convert(String source) { public Integer convert(String source) {
return Integer.valueOf(source); return Integer.valueOf(source);
} }
}]]> }</programlisting>
</programlisting>
</section> </section>
<section id="core-convert-ConverterFactory-SPI"> <section id="core-convert-ConverterFactory-SPI">
<title>ConverterFactory</title> <title>ConverterFactory</title>
<para> <para>
When you need to centralize the conversion logic for an entire class hierarchy, for example, when converting from String to java.lang.Enum objects, implement a ConverterFactory: When you need to centralize the conversion logic for an entire class hierarchy, for example, when converting from String to java.lang.Enum objects, implement a <interfacename>ConverterFactory</interfacename>ConverterFactory:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
package org.springframework.core.converter; package org.springframework.core.converter;
public interface ConverterFactory<S, R> { public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType); <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}]]> }]]></programlisting>
</programlisting>
<para> <para>
Parameterize S to be type you are converting from, and R to be base type defining the <emphasis>range</emphasis> of classes you can convert to. Parameterize S to be type you are converting from and R to be base type defining the <emphasis>range</emphasis> of classes you can convert to.
Then implement getConverter(Class&lt;T&gt;), where T is a subclass of R. Then implement getConverter(Class&lt;T&gt;), where T is a subclass of R.
</para> </para>
<para> <para>
Note the StringToEnum ConverterFactory as an example: Consider the <classname>StringToEnum</classname> ConverterFactory as an example:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
public class StringToEnumFactory implements ConverterFactory<String, Enum> { public class StringToEnumFactory implements ConverterFactory<String, Enum> {
@ -835,8 +831,7 @@ public class StringToEnumFactory implements ConverterFactory<String, Enum> {
return (T) Enum.valueOf(this.enumType, source.trim()); return (T) Enum.valueOf(this.enumType, source.trim());
} }
} }
}]]> }]]></programlisting>
</programlisting>
</section> </section>
<section id="core-convert-ConversionService-API"> <section id="core-convert-ConversionService-API">
<title>ConversionService API</title> <title>ConversionService API</title>
@ -852,10 +847,9 @@ public interface ConversionService {
<T> T convert(Object source, Class<T> targetType); <T> T convert(Object source, Class<T> targetType);
}]]> }]]></programlisting>
</programlisting>
<para> <para>
Most ConversionService implementations also implement <interface>ConverterRegistry</interface>, which provides a SPI for registering converters. Most ConversionService implementations also implement <interface>ConverterRegistry</interface>, which provides an SPI for registering converters.
Internally, a ConversionService implementation delegates to its registered Converters and ConverterFactories to carry out type conversion logic. Internally, a ConversionService implementation delegates to its registered Converters and ConverterFactories to carry out type conversion logic.
</para> </para>
<para> <para>
@ -871,8 +865,12 @@ public interface ConversionService {
In a Spring application, you typically configure a ConversionService instance per Spring container (or ApplicationContext). In a Spring application, you typically configure a ConversionService instance per Spring container (or ApplicationContext).
That ConversionService will be picked up by Spring and then used whenever a type conversion needs to be performed by the framework. That ConversionService will be picked up by Spring and then used whenever a type conversion needs to be performed by the framework.
You may also inject this ConversionService into any of your beans and invoke it directly. You may also inject this ConversionService into any of your beans and invoke it directly.
If no ConversionService is registered with Spring, the original PropertyEditor-based system is used.
</para> </para>
<note>
<para>
If no ConversionService is registered with Spring, the original PropertyEditor-based system is used.
</para>
</note>
<para> <para>
To register the DefaultConversionService with Spring, simply configure it as a bean with the id <code>conversionService</code>: To register the DefaultConversionService with Spring, simply configure it as a bean with the id <code>conversionService</code>:
</para> </para>
@ -880,7 +878,7 @@ public interface ConversionService {
<bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService" />]]> <bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService" />]]>
</programlisting> </programlisting>
<para> <para>
To override the default converter set with your own custom converter(s), set the <code>converters</code> property: To override the default set of converters with your own custom converter(s), set the <code>converters</code> property:
</para> </para>
<programlisting language="xml"><![CDATA[ <programlisting language="xml"><![CDATA[
<bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService"> <bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService">
@ -889,13 +887,12 @@ public interface ConversionService {
<bean class="example.MyCustomConverter" /> <bean class="example.MyCustomConverter" />
</list> </list>
</property> </property>
</bean>]]> </bean>]]></programlisting>
</programlisting>
</section> </section>
<section id="core-convert-programmatic-usage"> <section id="core-convert-programmatic-usage">
<title>Using a ConversionService programatically</title> <title>Using a ConversionService programatically</title>
<para> <para>
To work with a ConversionService instance programatically, simply inject a reference to it like you would any other bean: To work with a ConversionService instance programatically, simply inject a reference to it like you would for any other bean:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
@Service @Service
@ -909,8 +906,7 @@ public class MyService {
public void doIt() { public void doIt() {
this.conversionService.convert(...) this.conversionService.convert(...)
} }
}]]> }]]></programlisting>
</programlisting>
</section> </section>
</section> </section>
@ -919,9 +915,10 @@ public class MyService {
<para> <para>
<link linkend="core.convert"><filename>core.convert</filename></link> is a simple, general-purpose type conversion system. <link linkend="core.convert"><filename>core.convert</filename></link> is a simple, general-purpose type conversion system.
It addresses <emphasis>one-way</emphasis> conversion from one type to another and is not limited to just converting Strings. It addresses <emphasis>one-way</emphasis> conversion from one type to another and is not limited to just converting Strings.
As discussed in the previous section, a Spring Container can be configured to use this system when binding bean property values. As discussed in the previous section, a Spring Container can be configured to use this system to bind bean property values.
In addition, the Spring Expression Language (SpEL) uses this system to coerce Expression values. In addition, the Spring Expression Language (SpEL) uses this system to coerce Expression values.
For example, when SpEL needs to coerse a Short to a Long to fullfill an expression.setValue attempt, the core.convert system performs the coersion. For example, when SpEL needs to coerce a <classname>Short</classname> to a <classname>Long</classname> to fullfill an
<function>expression.setValue()</function> attempt, the core.convert system performs the coersion.
</para> </para>
<para> <para>
Now consider the type conversion requirements of a typical UI environment such as a web or desktop application. Now consider the type conversion requirements of a typical UI environment such as a web or desktop application.
@ -930,11 +927,11 @@ public class MyService {
To directly address this, Spring 3 introduces a new <emphasis>ui.format</emphasis> system that provides a simple and robust alternative to PropertyEditors in a UI environment. To directly address this, Spring 3 introduces a new <emphasis>ui.format</emphasis> system that provides a simple and robust alternative to PropertyEditors in a UI environment.
</para> </para>
<para> <para>
In general, use Converters when you need implement general-purpose type In general, use Converters when you need to implement general-purpose type
conversion logic, logic that may be invoked by the Spring Container, SpEL, conversion logic, logic that may be invoked by the Spring Container, SpEL,
or your own code as part of a <emphasis>one-way</emphasis> binding process. or your own code as part of a <emphasis>one-way</emphasis> binding process.
Use Formatters when you're working in a UI environment, such as an HTML form Use Formatters when you're working in a UI environment, such as an HTML form
of a web application, and need to apply <emphasis>two-way</emphasis> parsing, of a web application and need to apply <emphasis>two-way</emphasis> parsing,
formatting, and localization logic to form field values. formatting, and localization logic to form field values.
</para> </para>
<section id="ui-format-Formatter-SPI"> <section id="ui-format-Formatter-SPI">
@ -953,15 +950,14 @@ public interface Formatter<T> {
T parse(String formatted, Locale locale) throws ParseException; T parse(String formatted, Locale locale) throws ParseException;
}]]> }]]></programlisting>
</programlisting>
<para> <para>
To create your own Formatter, simply implement the interface above. To create your own Formatter, simply implement the interface above.
Parameterize T to be the type of Object you are formatting; for example, <classname>java.lang.BigDecimal</classname>. Parameterize T to be the type of object you are formatting, for example, <classname>java.lang.BigDecimal</classname>.
Implement the <methodname>format</methodname> operation to format an instance of T for display in the client locale. Implement the <methodname>format</methodname> operation to format an instance of T for display in the client locale.
Implement the <methodname>parse</methodname> operation to parse an instance of T from the formatted representation returned from the client locale. Implement the <methodname>parse</methodname> operation to parse an instance of T from the formatted representation returned from the client locale.
Your Formatter should throw a ParseException if a parse attempt fails. Your Formatter should throw a ParseException if a parse attempt fails.
Take care to ensure your Formatter implementation is thread safe. Take care to ensure your Formatter implementation is thread-safe.
</para> </para>
<para> <para>
Several Formatter implementations are provided in subpackages of <filename>ui.format</filename> as a convenience. Several Formatter implementations are provided in subpackages of <filename>ui.format</filename> as a convenience.
@ -969,7 +965,7 @@ public interface Formatter<T> {
The <filename>number</filename> package provides a DecimalFormatter, IntegerFormatter, CurrencyFormatter, and PercentFormatter to format java.lang.Number objects using a java.text.NumberFormat. The <filename>number</filename> package provides a DecimalFormatter, IntegerFormatter, CurrencyFormatter, and PercentFormatter to format java.lang.Number objects using a java.text.NumberFormat.
</para> </para>
<para> <para>
Note DateFormatter as an example Formatter implementation: Consider <classname>DateFormatter</classname> as an example <interfacename>Formatter</interfacename> implementation:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
package org.springframework.ui.format.date; package org.springframework.ui.format.date;
@ -1002,8 +998,7 @@ public final class DateFormatter implements Formatter<Date> {
return dateFormat; return dateFormat;
} }
}]]> }]]></programlisting>
</programlisting>
<para> <para>
The Spring team welcomes community-driven Formatter contributions; see <ulink url="http://jira.springframework.org">http://jira.springframework.org</ulink> to contribute. The Spring team welcomes community-driven Formatter contributions; see <ulink url="http://jira.springframework.org">http://jira.springframework.org</ulink> to contribute.
In particular, the team hopes to integrate support for Joda Time and Money Formatters in the future. In particular, the team hopes to integrate support for Joda Time and Money Formatters in the future.
@ -1019,9 +1014,7 @@ public final class DateFormatter implements Formatter<Date> {
@Formatted(MoneyFormatter.class) @Formatted(MoneyFormatter.class)
public class Money { public class Money {
... ...
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
The example above says <emphasis>"Money objects should be formatted by a MoneyFormatter"</emphasis>. The example above says <emphasis>"Money objects should be formatted by a MoneyFormatter"</emphasis>.
With this configuation, whenever a field is of type Money, MoneyFormatter will format the field value. With this configuation, whenever a field is of type Money, MoneyFormatter will format the field value.
@ -1038,9 +1031,7 @@ public class Money {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Formatted(CurrencyFormatter.class) @Formatted(CurrencyFormatter.class)
public @interface Currency { public @interface Currency {
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
Then, to trigger formatting, simply annotate a model property with the annotation: Then, to trigger formatting, simply annotate a model property with the annotation:
</para> </para>
@ -1050,9 +1041,7 @@ public class MyModel {
@Currency @Currency
private BigDecimal amount; private BigDecimal amount;
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
Custom annotations like @Currency can also be annotated with JSR-303 constraint annotations to specify declarative validation constraints. Custom annotations like @Currency can also be annotated with JSR-303 constraint annotations to specify declarative validation constraints.
For example: For example:
@ -1063,9 +1052,7 @@ public class MyModel {
@Formatted(CurrencyFormatter.class) @Formatted(CurrencyFormatter.class)
@Constraint(validatedBy = CurrencyValidator.class) @Constraint(validatedBy = CurrencyValidator.class)
public @interface Currency { public @interface Currency {
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
Given the example above, on form postback any @Currency properties will first be parsed by CurrencyFormatter, then validated by CurrencyValidator. Given the example above, on form postback any @Currency properties will first be parsed by CurrencyFormatter, then validated by CurrencyValidator.
</para> </para>
@ -1082,9 +1069,7 @@ public interface AnnotationFormatterFactory<A extends Annotation, T> {
Formatter<T> getFormatter(A annotation); Formatter<T> getFormatter(A annotation);
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
The example implementation below binds a @DecimalFormat instance to a Formatter instance. The example implementation below binds a @DecimalFormat instance to a Formatter instance.
This particular annotation allows the NumberFormat pattern to be configured. This particular annotation allows the NumberFormat pattern to be configured.
@ -1097,11 +1082,9 @@ public class DecimalAnnotationFormatterFactory implements AnnotationFormatterFac
formatter.setPattern(annotation.value()); formatter.setPattern(annotation.value());
return formatter; return formatter;
} }
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
Then, to trigger, simply annotate a property as a @DecimalFormat in your model: Then, to trigger formatting, simply annotate a property with @DecimalFormat in your model:
</para> </para>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
public class MyModel { public class MyModel {
@ -1109,9 +1092,7 @@ public class MyModel {
@DecimalFormat("#,###") @DecimalFormat("#,###")
private BigDecimal decimal; private BigDecimal decimal;
} }]]></programlisting>
]]>
</programlisting>
</section> </section>
</section> </section>
<section id="ui-format-FormatterRegistry-SPI"> <section id="ui-format-FormatterRegistry-SPI">
@ -1135,8 +1116,7 @@ public interface FormatterRegistry {
void add(AnnotationFormatterFactory<?, ?> factory); void add(AnnotationFormatterFactory<?, ?> factory);
}]]> }]]></programlisting>
</programlisting>
<para> <para>
As shown above, Formatters may be registered by field type or annotation. As shown above, Formatters may be registered by field type or annotation.
<classname>GenericFormatterRegistry</classname> is the implementation suitable for use in most UI binding environments. <classname>GenericFormatterRegistry</classname> is the implementation suitable for use in most UI binding environments.
@ -1146,11 +1126,15 @@ public interface FormatterRegistry {
<section id="ui-format-configuring-FormatterRegistry"> <section id="ui-format-configuring-FormatterRegistry">
<title>Configuring a FormatterRegistry</title> <title>Configuring a FormatterRegistry</title>
<para> <para>
A FormatterRegistry is a stateless object designed to be instantiated at application startup, then shared between multiple threads. A FormatterRegistry is designed to be instantiated at application startup, then shared between multiple threads.
In a Spring MVC application, you configure a FormatterRegistry as a property of the WebBindingInitializer. In a Spring MVC application, you configure a FormatterRegistry as a property of the WebBindingInitializer.
The FormatterRegistry will then be configured whenever a DataBinder is created by Spring MVC to bind and render model properties. The FormatterRegistry will then be configured whenever a DataBinder is created by Spring MVC to bind and render model properties.
If no FormatterRegistry is configured, the original PropertyEditor-based system is used.
</para> </para>
<note>
<para>
If no FormatterRegistry is configured, the original PropertyEditor-based system is used.
</para>
</note>
<para> <para>
To register a FormatterRegistry with Spring MVC, simply configure it as a property of a custom WebBindingInitializer injected into the To register a FormatterRegistry with Spring MVC, simply configure it as a property of a custom WebBindingInitializer injected into the
Spring MVC AnnotationMethodHandlerAdapter: Spring MVC AnnotationMethodHandlerAdapter:
@ -1205,11 +1189,10 @@ public interface FormatterRegistry {
} }
... ...
} }]]></programlisting>
]]>
</programlisting>
<para> <para>
This applies the Formatter to the field, and overrides any Formatter that would have been applied by field type or annotation. This applies the Formatter to the field and overrides any Formatter
that would have been applied by field type or annotation.
</para> </para>
</section> </section>
</section> </section>
@ -1236,8 +1219,7 @@ public interface FormatterRegistry {
public class Person { public class Person {
private String name; private String name;
private int age; private int age;
}]]> }]]></programlisting>
</programlisting>
<para> <para>
JSR-303 allows you to define declarative validation constraints against such properties: JSR-303 allows you to define declarative validation constraints against such properties:
</para> </para>
@ -1251,8 +1233,7 @@ public class Person {
@Min(0) @Min(0)
private int age; private int age;
}]]> }]]></programlisting>
</programlisting>
<para> <para>
When an instance of this class is validated by a JSR-303 Validator, these constraints will be enforced. When an instance of this class is validated by a JSR-303 Validator, these constraints will be enforced.
</para> </para>
@ -1318,8 +1299,7 @@ public class MyService {
@Autowired @Autowired
private Validator validator; private Validator validator;
}]]> }]]></programlisting>
</programlisting>
</section> </section>
<section id="validation.beanvalidation.spring.constraints"> <section id="validation.beanvalidation.spring.constraints">
<title>Configuring Custom Constraints</title> <title>Configuring Custom Constraints</title>
@ -1342,8 +1322,7 @@ public class MyService {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class) @Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint { public @interface MyConstraint {
}]]> }]]></programlisting>
</programlisting>
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
@ -1353,8 +1332,7 @@ public class MyConstraintValidator implements ConstraintValidator {
private Foo aDependency; private Foo aDependency;
... ...
}]]> }]]></programlisting>
</programlisting>
<para> <para>
As you can see, a ConstraintValidator implementation may have its dependencies @Autowired like any other Spring bean. As you can see, a ConstraintValidator implementation may have its dependencies @Autowired like any other Spring bean.
</para> </para>
@ -1437,8 +1415,7 @@ public class MyController {
@RequestMapping("/foo", method=RequestMethod.POST) @RequestMapping("/foo", method=RequestMethod.POST)
public void processFoo(@Valid Foo foo) { ... } public void processFoo(@Valid Foo foo) { ... }
}]]> }]]></programlisting>
</programlisting>
<para> <para>
Second, you may call setValidator(Validator) on the global WebBindingInitializer. Second, you may call setValidator(Validator) on the global WebBindingInitializer.
This allows you to configure a Validator instance across all @Controllers: This allows you to configure a Validator instance across all @Controllers:
@ -1452,8 +1429,7 @@ public class MyController {
<property name="validator" ref="validator" /> <property name="validator" ref="validator" />
</bean> </bean>
</property> </property>
</bean>]]> </bean>]]></programlisting>
</programlisting>
</section> </section>
<section id="validation.mvc.jsr303"> <section id="validation.mvc.jsr303">
<title>Configuring a JSR-303 Validator for use by Spring MVC</title> <title>Configuring a JSR-303 Validator for use by Spring MVC</title>
@ -1477,8 +1453,7 @@ public class MyController {
</bean> </bean>
<!-- Creates the JSR-303 Validator --> <!-- Creates the JSR-303 Validator -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />]]> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />]]></programlisting>
</programlisting>
<para> <para>
With this configuration, anytime a @Valid @Controller input is encountered, it will be validated by the JSR-303 provider. With this configuration, anytime a @Valid @Controller input is encountered, it will be validated by the JSR-303 provider.
JSR-303, in turn, will enforce any constraints declared against the input. JSR-303, in turn, will enforce any constraints declared against the input.