converter / formatter docs initial commit

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1798 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Keith Donald 2009-09-02 23:34:01 +00:00
parent 83ed9fb3a7
commit aff628f177
1 changed files with 324 additions and 9 deletions

View File

@ -26,17 +26,16 @@
<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
will not 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>
in order. We will explain the <interfacename>BeanWrapper</interfacename> in this chapter since, if you were
going to use it at all, you would most likely do so when trying to bind data to objects.</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>
<para>Spring's DataBinder and the lower-level BeanWrapper both use PropertyEditors to parse and format property values.
The <interfacename>PropertyEditor</interfacename> concept is part of the JavaBeans specification, and is also explained in this chapter.
Spring 3 introduces a "core.convert" package that provides a general type conversion facility, as well as a higher-level "format" package for formatting UI field values.
These new packages may be used as simpler alternatives to PropertyEditors, and will also be discussed in this chapter.</para>
</section>
<section id="validator">
@ -749,4 +748,320 @@ public final class CustomPropertyEditorRegistrar implements PropertyEditorRegist
</section>
</section>
<section id="core.convert">
<title>Spring 3 Type Conversion</title>
<para>
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.
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 coersion is needed.
</para>
<section id="core-convert-Converter-SPI">
<title>Converter SPI</title>
<para>
The SPI to implement type conversion logic is simple and strongly typed:
</para>
<programlisting language="java"><![CDATA[
package org.springframework.core.converter;
public interface Converter<S, T> {
T convert(S source) throws Exception;
}]]>
</programlisting>
<para>
To create your own Converter, simply implement the Converter interface.
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.
Your Converter may throw any Exception if conversion fails.
An IllegalArgumentException is often thrown to report an invalid source value.
Take special care to ensure your Converter implementation is thread safe.
</para>
<para>
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.
Note StringToInteger as an example Converter implementation:
</para>
<programlisting language="java"><![CDATA[
package org.springframework.core.convert.converters;
public class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}]]>
</programlisting>
</section>
<section id="core-convert-ConversionService-API">
<title>ConversionService API</title>
<para>
The ConversionService defines a public API for executing type conversion logic at runtime.
Converters are <emphasis>always</emphasis> executed behind this API.
User code should not depend on the Converter SPI.
</para>
<programlisting language="java"><![CDATA[
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
}]]>
</programlisting>
<para>
Most ConversionService implementations also implement <interface>ConverterRegistry</interface>, which provides a SPI for registering converters.
Internally, a ConversionService implementation delegates to its registered Converters to carry out type conversion logic.
</para>
<para>
Two ConversionService implementations are provided with the system in the <filename>core.convert.support</filename> package.
<classname>GenericConversionService</classname> is a generic implementation designed to be explicitly configured, either programatically or declaratively as a Spring bean.
<classname>DefaultConversionService</classname> is a subclass that pre-registers the common Converters in the <filename>core.converter</filename> package as a convenience.
</para>
</section>
<section id="core-convert-Spring-config">
<title>Configuring a ConversionService</title>
<para>
A ConversionService is a stateless object designed to be instantiated on application startup, then shared between multiple threads.
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.
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>
To register the DefaultConversionService with Spring, simply configure it as a bean with the id <code>conversionService</code>:
</para>
<programlisting language="xml"><![CDATA[
<bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService" />]]>
</programlisting>
<para>
To override the default converter set with your own custom converter(s), set the <code>converters</code> property:
</para>
<programlisting language="xml"><![CDATA[
<bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService">
<property name="converters">
<list>
<bean class="example.MyCustomConverter" />
</list>
</property>
</bean>]]>
</programlisting>
</section>
<section id="core-convert-programmatic-usage">
<title>Using a ConversionService programatically</title>
<para>
To work with a ConversionService instance programatically, simply inject a reference to it like you would any other bean:
</para>
<programlisting language="java"><![CDATA[
@Service
public class MyService {
@Autowired
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void doIt() {
this.conversionService.convert(...)
}
}]]>
</programlisting>
</section>
</section>
<section id="ui.format">
<title>Spring 3 UI Field Formatting</title>
<para>
The <filename>core.convert</filename> 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.
As discussed in the previous section, a Spring Container can be configured to use this system when binding bean property 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 a expression.setValue attempt, the core.convert system performs the coersion.
</para>
<para>
Now consider the type conversion requirements of a typical UI environment such as a web or desktop application.
In such environments, you typically convert <emphasis>from String</emphasis> to support the postback process, as well as back <emphasis>to String</emphasis> to support the rendering process.
The more general <emphasis>core.convert</emphasis> system does not address this specific scenario directly.
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>
In general, use Converters when you need implement general-purpose type 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.
Use Formatters when you're working in a UI environment such as a HTML form of a web application, and need to apply <emphasis>two-way</emphasis> parsing, formatting, and localization logic to form field values.
</para>
<section id="ui-format-Formatter-SPI">
<title>Formatter SPI</title>
<para>
The SPI to implement UI formatting logic is simple and strongly typed:
</para>
<programlisting language="java"><![CDATA[
package org.springframework.ui.format;
import java.text.ParseException;
public interface Formatter<T> {
String format(T object, Locale locale);
T parse(String formatted, Locale locale) throws ParseException;
}]]>
</programlisting>
<para>
To create your own Formatter, simply implement the Formatter interface above.
Parameterize T to be the type of Object you are formatting; for example, java.lang.BigDecimal.
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.
Your Formatter should throw a ParseException if a parse attempt fails.
Take special care to ensure your Formatter implementation is thread safe.
</para>
<para>
Several Formatter implementations are provided in subpackages of <filename>ui.format</filename> as a convenience.
The <filename>date</filename> package provides a DateFormatter to format java.util.Date objects with a java.text.DateFormat.
The <filename>number</filename> package provides a DecimalFormatter, IntegerFormatter, CurrencyFormatter, and PercentFormatter for formatting java.lang.Number objects using a java.text.NumberFormat.
Note DateFormatter as an example Formatter implementation:
</para>
<programlisting language="java"><![CDATA[
package org.springframework.ui.format.date;
public final class DateFormatter implements Formatter<Date> {
private String pattern;
public DateFormatter(String pattern) {
this.pattern = pattern;
}
public String format(Date date, Locale locale) {
if (date == null) {
return "";
}
return getDateFormat(locale).format(date);
}
public Date parse(String formatted, Locale locale) throws ParseException {
if (formatted.length() == 0) {
return null;
}
return getDateFormat(locale).parse(formatted);
}
protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale);
dateFormat.setLenient(false);
return dateFormat;
}
}]]>
</programlisting>
</section>
<section id="ui-format-Formatted-Annotation">
<title>@Formatted Annotation</title>
<para>
The @Formatted annotation allows you to easily configure the Formatter implementation to use for one or more of your model classes.
To use this feature, simply annotate your class as @Formatted and specify the Formatter implementation to use as the annotation value:
</para>
<programlisting language="java"><![CDATA[
@Formatted(MoneyFormatter.class)
public class Money {
...
}
]]>
</programlisting>
<para>
The example above says <emphasis>"Money objects should be formatted by MoneyFormatter"</emphasis>.
With this configuation, whenever a field binds to a Money property, the MoneyFormatter implementation will format the field value.
When referenced by a @Formatted annotation, a Formatter implementation must declare a public default constructor.
</para>
</section>
<section id="ui-format-AnnotationFormatterFactory">
<title>Custom Format Annotations</title>
<para>
The presence of field or method annotations on properties of your model objects can also trigger field-specific formatting logic.
The binding between a custom annotation and a specific Formatter instance is made by implementing a custom AnnotationFormatterFactory:
</para>
<programlisting language="java"><![CDATA[
package org.springframework.ui.format;
public interface AnnotationFormatterFactory<A extends Annotation, T> {
Formatter<T> getFormatter(A annotation);
}
]]>
</programlisting>
<para>
The example implementation below binds a custom @DecimalFormat annotation to a Number Formatter instance.
The annotation allows the format pattern to be configured as its value.
</para>
<programlisting language="java"><![CDATA[
package example.format;
public class DecimalAnnotationFormatterFactory implements AnnotationFormatterFactory<DecimalFormat, Number> {
Formatter<Number> getFormatter(DecimalFormat annotation) {
DecimalFormatter formatter = DecimalFormatter();
formatter.setPattern(annotation.value());
return formatter;
}
}
]]>
</programlisting>
<para>
Then, to trigger somewhere in your model:
</para>
<programlisting language="java"><![CDATA[
package example.app;
public class MyModel {
@DecimalFormat("#,###")
private BigDecimal decimal;
}
]]>
</programlisting>
</section>
<section id="ui-format-FormatterRegistry-SPI">
<title>FormatterRegistry SPI</title>
<para>
Formatters are often registered in a FormatterRegistry.
A DataBinder uses this registry to resolve the Formatter to use for a specific field.
This allows you to configure default Formatting rules centrally, rather than duplicating such configuration across your UI Controllers.
For example, you might want to enforce that all Date fields are formatted a certain way, or fields with a specific annotation are formatted in a certain way.
With a shared FormatterRegistry, you define these rules once and they are applied whenever field formatting is needed.
</para>
<para>
Review the FormatterRegistry SPI below:
</para>
<programlisting language="java"><![CDATA[
public interface FormatterRegistry {
void add(Class<?> type, Formatter<?> targetFormatter);
void add(AnnotationFormatterFactory<?, ?> factory);
}]]>
</programlisting>
<para>
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.
This implementation may be configured programatically or declatively as a Spring bean.
</para>
</section>
<section id="ui-format-configuring-FormatterRegistry">
<title>Configuring a FormatterRegistry</title>
<para>
</para>
<para>
</para>
<programlisting language="java"><![CDATA[
]]>
</programlisting>
</section>
<section id="ui-format-customizing-DataBinder">
<title>Registering field-specific Formatters</title>
<para>
</para>
<para>
</para>
<programlisting language="java"><![CDATA[
]]>
</programlisting>
</section>
</section>
</chapter>