SPR-7912 Add tests for FormattingConversionServiceFactoryBean, update reference docs, and remove mvc:formatters
This commit is contained in:
parent
149348c907
commit
abff2b959b
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.format.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.format.AnnotationFormatterFactory;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.FormatterRegistrar;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.Parser;
|
||||
import org.springframework.format.Printer;
|
||||
import org.springframework.format.annotation.NumberFormat;
|
||||
import org.springframework.format.annotation.NumberFormat.Style;
|
||||
|
||||
/**
|
||||
* Test fixture for FormattingConversionServiceFactoryBean.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class FormattingConversionServiceFactoryBeanTests {
|
||||
|
||||
@Test
|
||||
public void testDefaultFormattersOn() throws Exception {
|
||||
FormattingConversionServiceFactoryBean factory = new FormattingConversionServiceFactoryBean();
|
||||
factory.afterPropertiesSet();
|
||||
FormattingConversionService fcs = factory.getObject();
|
||||
TypeDescriptor descriptor = new TypeDescriptor(TestBean.class.getDeclaredField("percent"));
|
||||
Object value = fcs.convert("5%", TypeDescriptor.valueOf(String.class), descriptor);
|
||||
assertEquals(.05, value);
|
||||
value = fcs.convert(.05, descriptor, TypeDescriptor.valueOf(String.class));
|
||||
assertEquals("5%", value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultFormattersOff() throws Exception {
|
||||
FormattingConversionServiceFactoryBean factory = new FormattingConversionServiceFactoryBean();
|
||||
factory.setRegisterDefaultFormatters(false);
|
||||
factory.afterPropertiesSet();
|
||||
FormattingConversionService fcs = factory.getObject();
|
||||
TypeDescriptor descriptor = new TypeDescriptor(TestBean.class.getDeclaredField("percent"));
|
||||
try {
|
||||
fcs.convert("5%", TypeDescriptor.valueOf(String.class), descriptor);
|
||||
fail("This format should not be parseable");
|
||||
} catch (ConversionFailedException e) {
|
||||
assertTrue(e.getCause() instanceof NumberFormatException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomFormatter() throws Exception {
|
||||
FormattingConversionServiceFactoryBean factory = new FormattingConversionServiceFactoryBean();
|
||||
Set<Object> formatters = new HashSet<Object>();
|
||||
formatters.add(new TestBeanFormatter());
|
||||
formatters.add(new SpecialIntAnnotationFormatterFactory());
|
||||
factory.setFormatters(formatters);
|
||||
factory.afterPropertiesSet();
|
||||
FormattingConversionService fcs = factory.getObject();
|
||||
|
||||
TestBean testBean = fcs.convert("5", TestBean.class);
|
||||
assertEquals(5, testBean.getSpecialInt());
|
||||
assertEquals("5", fcs.convert(testBean, String.class));
|
||||
|
||||
TypeDescriptor descriptor = new TypeDescriptor(TestBean.class.getDeclaredField("specialInt"));
|
||||
Object value = fcs.convert(":5", TypeDescriptor.valueOf(String.class), descriptor);
|
||||
assertEquals(5, value);
|
||||
value = fcs.convert(5, descriptor, TypeDescriptor.valueOf(String.class));
|
||||
assertEquals(":5", value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatterRegistrar() throws Exception {
|
||||
FormattingConversionServiceFactoryBean factory = new FormattingConversionServiceFactoryBean();
|
||||
Set<FormatterRegistrar> registrars = new HashSet<FormatterRegistrar>();
|
||||
registrars.add(new TestFormatterRegistrar());
|
||||
factory.setFormatterRegistrars(registrars);
|
||||
factory.afterPropertiesSet();
|
||||
FormattingConversionService fcs = factory.getObject();
|
||||
|
||||
TestBean testBean = fcs.convert("5", TestBean.class);
|
||||
assertEquals(5, testBean.getSpecialInt());
|
||||
assertEquals("5", fcs.convert(testBean, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidFormatter() throws Exception {
|
||||
FormattingConversionServiceFactoryBean factory = new FormattingConversionServiceFactoryBean();
|
||||
Set<Object> formatters = new HashSet<Object>();
|
||||
formatters.add(new Object());
|
||||
factory.setFormatters(formatters);
|
||||
try {
|
||||
factory.afterPropertiesSet();
|
||||
fail("Expected formatter to be rejected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface SpecialInt {
|
||||
}
|
||||
|
||||
private static class TestBean {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@NumberFormat(style = Style.PERCENT)
|
||||
private double percent;
|
||||
|
||||
@SpecialInt
|
||||
private int specialInt;
|
||||
|
||||
public int getSpecialInt() {
|
||||
return specialInt;
|
||||
}
|
||||
|
||||
public void setSpecialInt(int field) {
|
||||
this.specialInt = field;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestBeanFormatter implements Formatter<TestBean> {
|
||||
|
||||
public String print(TestBean object, Locale locale) {
|
||||
return String.valueOf(object.getSpecialInt());
|
||||
}
|
||||
|
||||
public TestBean parse(String text, Locale locale) throws ParseException {
|
||||
TestBean object = new TestBean();
|
||||
object.setSpecialInt(Integer.parseInt(text));
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SpecialIntAnnotationFormatterFactory implements AnnotationFormatterFactory<SpecialInt> {
|
||||
|
||||
private final Set<Class<?>> fieldTypes = new HashSet<Class<?>>(1);
|
||||
|
||||
public SpecialIntAnnotationFormatterFactory() {
|
||||
fieldTypes.add(Integer.class);
|
||||
}
|
||||
|
||||
public Set<Class<?>> getFieldTypes() {
|
||||
return fieldTypes;
|
||||
}
|
||||
|
||||
public Printer<?> getPrinter(SpecialInt annotation, Class<?> fieldType) {
|
||||
return new Printer<Integer>() {
|
||||
public String print(Integer object, Locale locale) {
|
||||
return ":" + object.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Parser<?> getParser(SpecialInt annotation, Class<?> fieldType) {
|
||||
return new Parser<Integer>() {
|
||||
public Integer parse(String text, Locale locale) throws ParseException {
|
||||
return Integer.parseInt(text.substring(1));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestFormatterRegistrar implements FormatterRegistrar {
|
||||
|
||||
public void registerFormatters(FormatterRegistry registry) {
|
||||
registry.addFormatter(new TestBeanFormatter());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,25 +38,6 @@
|
|||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="formatters">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Registers custom Formatter and AnnotationFormatterFactory types with the FormattingConversionService.
|
||||
Specifying custom formatters does not cancel the ones already built-in.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="beans:bean" minOccurs="1" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The Formatter or the AnnotationFormatterFactory bean definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:all>
|
||||
<xsd:attribute name="conversion-service" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
|
|
|
|||
|
|
@ -19,34 +19,16 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.format.AnnotationFormatterFactory;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.Parser;
|
||||
import org.springframework.format.Printer;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver;
|
||||
|
|
@ -85,21 +67,6 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
verifyMessageConverters(appContext.getBean(AnnotationMethodHandlerExceptionResolver.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatters() throws Exception {
|
||||
FormattingConversionService conversionService = appContext.getBean(FormattingConversionService.class);
|
||||
assertNotNull(conversionService);
|
||||
|
||||
TestBean testBean = conversionService.convert("5", TestBean.class);
|
||||
assertEquals(TestBeanFormatter.class.getSimpleName() + " should have been used.", 5, testBean.getField());
|
||||
assertEquals("5", conversionService.convert(testBean, String.class));
|
||||
|
||||
TypeDescriptor intTypeDescriptor = new TypeDescriptor(TestBean.class.getDeclaredField("anotherField"));
|
||||
Object actual = conversionService.convert(">>5<<", TypeDescriptor.valueOf(String.class), intTypeDescriptor);
|
||||
assertEquals(TestBeanAnnotationFormatterFactory.class.getSimpleName() + " should have been used", 5, actual);
|
||||
actual = conversionService.convert(5, intTypeDescriptor, TypeDescriptor.valueOf(String.class));
|
||||
assertEquals(">>5<<", actual);
|
||||
}
|
||||
|
||||
private void verifyMessageConverters(Object bean) {
|
||||
assertNotNull(bean);
|
||||
|
|
@ -124,102 +91,4 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestWebArgumentResolver implements WebArgumentResolver {
|
||||
|
||||
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
|
||||
throw new IllegalStateException("Not expected to be invoked");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class AnotherTestWebArgumentResolver implements WebArgumentResolver {
|
||||
|
||||
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
|
||||
throw new IllegalStateException("Not expected to be invoked");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestBeanFormatter implements Formatter<TestBean> {
|
||||
|
||||
public String print(TestBean object, Locale locale) {
|
||||
return String.valueOf(object.getField());
|
||||
}
|
||||
|
||||
public TestBean parse(String text, Locale locale) throws ParseException {
|
||||
TestBean object = new TestBean();
|
||||
object.setField(Integer.parseInt(text));
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestBeanAnnotationFormatterFactory implements AnnotationFormatterFactory<SpecialIntFormat> {
|
||||
|
||||
private final Set<Class<?>> fieldTypes = new HashSet<Class<?>>(1);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public TestBeanAnnotationFormatterFactory() {
|
||||
fieldTypes.add(Integer.class);
|
||||
}
|
||||
|
||||
public Set<Class<?>> getFieldTypes() {
|
||||
return fieldTypes;
|
||||
}
|
||||
|
||||
public Printer<?> getPrinter(SpecialIntFormat annotation, Class<?> fieldType) {
|
||||
return new Printer<Integer>() {
|
||||
public String print(Integer object, Locale locale) {
|
||||
return ">>" + object.toString() + "<<";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Parser<?> getParser(SpecialIntFormat annotation, Class<?> fieldType) {
|
||||
return new Parser<Integer>() {
|
||||
public Integer parse(String text, Locale locale) throws ParseException {
|
||||
if (!text.startsWith(">>") || !text.endsWith("<<") || (text.length() < 5)) {
|
||||
throw new ParseException(text + " is not in the expected format '>>intValue<<'", 0);
|
||||
}
|
||||
return Integer.parseInt(text.substring(2,3));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestBean {
|
||||
|
||||
private int field;
|
||||
|
||||
@SpecialIntFormat
|
||||
private int anotherField;
|
||||
|
||||
public int getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public void setField(int field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int getAnotherField() {
|
||||
return anotherField;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void setAnotherField(int anotherField) {
|
||||
this.anotherField = anotherField;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
private @interface SpecialIntFormat {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@
|
|||
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
|
||||
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
|
||||
</mvc:message-converters>
|
||||
<mvc:formatters>
|
||||
<bean class="org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParserTests$TestBeanFormatter"/>
|
||||
<bean class="org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParserTests$TestBeanAnnotationFormatterFactory"/>
|
||||
</mvc:formatters>
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<bean id="messageCodesResolver"
|
||||
|
|
|
|||
|
|
@ -1279,7 +1279,7 @@ public void handle(@RequestBody String body, Writer writer) throws IOException {
|
|||
the <classname>AnnotationMethodHandlerAdapter</classname> is extended
|
||||
to support the <classname>@RequestBody</classname> and has the
|
||||
following <interfacename>HttpMessageConverters</interfacename>
|
||||
registered by default:</para>
|
||||
registered by default if not using the MVC namespace:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
|
|
@ -1301,16 +1301,12 @@ public void handle(@RequestBody String body, Writer writer) throws IOException {
|
|||
<para><classname>SourceHttpMessageConverter</classname> converts
|
||||
to/from a javax.xml.transform.Source.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>MarshallingHttpMessageConverter</classname>
|
||||
converts to/from an object using the
|
||||
<classname>org.springframework.oxm</classname> package.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>For more information on these converters, see <link
|
||||
linkend="rest-message-conversion">Message Converters</link>.</para>
|
||||
linkend="rest-message-conversion">Message Converters</link>.
|
||||
Also see <xref linkend="mvc-annotation-driven"/> for information
|
||||
on the default message converters set up by the MVC namespace.</para>
|
||||
|
||||
<para>The <classname>MarshallingHttpMessageConverter</classname>
|
||||
requires a <interfacename>Marshaller</interfacename> and
|
||||
|
|
@ -3313,13 +3309,69 @@ public class SimpleController {
|
|||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Support for reading and writing XML, if JAXB is present on the classpath.
|
||||
HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Support for reading and writing JSON, if Jackson is present on the classpath.
|
||||
This is the complete list of HttpMessageConverters set up by mvc:annotation-driven:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><classname>ByteArrayHttpMessageConverter</classname>
|
||||
converts byte arrays.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>StringHttpMessageConverter</classname> converts
|
||||
strings.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>ResourceHttpMessageConverter</classname> converts
|
||||
to/from <classname>org.springframework.core.io.Resource</classname>
|
||||
for all media types.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>SourceHttpMessageConverter</classname> converts
|
||||
to/from a <classname>javax.xml.transform.Source</classname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>FormHttpMessageConverter</classname> converts
|
||||
form data to/from a <classname>MultiValueMap<String, String></classname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>Jaxb2RootElementHttpMessageConverter</classname>
|
||||
converts Java objects to/from XML -- added if JAXB2 is present
|
||||
on the classpath.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>MappingJacksonHttpMessageConverter</classname>
|
||||
converts to/from JSON -- added if Jackson is present on the classpath.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>AtomFeedHttpMessageConverter</classname>
|
||||
converts Atom feeds -- added if Rome is present on the classpath.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>RssChannelHttpMessageConverter</classname>
|
||||
converts RSS feeds -- added if Rome is present on the classpath.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
This list of HttpMessageConverters used can be replaced through
|
||||
the mvc:message-converters sub-element of mvc:annotation-driven.
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
A typical usage is shown below:
|
||||
|
|
|
|||
|
|
@ -172,17 +172,23 @@
|
|||
|
||||
<surname>Gierke</surname>
|
||||
</author>
|
||||
|
||||
<author>
|
||||
<firstname>Rossen</firstname>
|
||||
<surname>Stoyanchev</surname>
|
||||
</author>
|
||||
|
||||
</authorgroup>
|
||||
|
||||
<copyright>
|
||||
<year>2004-2010</year>
|
||||
<year>2004-2011</year>
|
||||
|
||||
<holder>Rod Johnson, Juergen Hoeller, Keith Donald, Colin Sampaleanu,
|
||||
Rob Harrop, Alef Arendsen, Thomas Risberg, Darren Davison, Dmitriy
|
||||
Kopylenko, Mark Pollack, Thierry Templier, Erwin Vervaet, Portia Tung,
|
||||
Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
|
||||
Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo,
|
||||
Andy Clement, Dave Syer, Oliver Gierke</holder>
|
||||
Andy Clement, Dave Syer, Oliver Gierke, Rossen Stoyanchev</holder>
|
||||
</copyright>
|
||||
|
||||
<legalnotice>
|
||||
|
|
|
|||
|
|
@ -1350,41 +1350,72 @@ public interface AnnotationFormatterFactory<A extends Annotation> {
|
|||
<section id="format-FormatterRegistry-SPI">
|
||||
<title>FormatterRegistry SPI</title>
|
||||
|
||||
<para> At runtime, Formatters are registered in a FormatterRegistry. The
|
||||
FormatterRegistry SPI allows you to configure Formatting rules
|
||||
centrally, instead of duplicating such configuration across your
|
||||
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 formatting is needed. </para>
|
||||
<para> The FormatterRegistry is an SPI for registering formatters and
|
||||
converters. <classname>FormattingConversionService</classname> is
|
||||
an implementation of FormatterRegistry suitable for most environments.
|
||||
This implementation may be configured programatically or declaratively
|
||||
as a Spring bean using
|
||||
<classname>FormattingConversionServiceFactoryBean</classname>.
|
||||
Because this implemementation also implements
|
||||
<classname>ConversionService</classname>, it can be directly
|
||||
configured for use with Spring's DataBinder and the Spring Expression
|
||||
Language (SpEL).
|
||||
</para>
|
||||
|
||||
<para> Review the FormatterRegistry SPI below: </para>
|
||||
|
||||
<programlisting language="java"><![CDATA[package org.springframework.format;
|
||||
|
||||
public interface FormatterRegistry {
|
||||
public interface FormatterRegistry extends ConverterRegistry {
|
||||
|
||||
void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser);
|
||||
|
||||
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
|
||||
|
||||
void addFormatterForFieldType(Formatter<?> formatter);
|
||||
|
||||
void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory);
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para> As shown above, Formatters can be registered by fieldType or
|
||||
annotation. <classname>FormattingConversionService</classname> is the
|
||||
implementation of <classname>FormatterRegistry</classname> suitable for
|
||||
most environments. This implementation may be configured
|
||||
programatically, or declaratively as a Spring bean using
|
||||
<classname>FormattingConversionServiceFactoryBean</classname>. Because
|
||||
this implemementation also implements
|
||||
<classname>ConversionService</classname>, it can be directly configured
|
||||
for use with Spring's DataBinder and the Spring Expression Language
|
||||
(SpEL). </para>
|
||||
annotation.
|
||||
</para>
|
||||
<para> The FormatterRegistry SPI allows you to configure Formatting rules
|
||||
centrally, instead of duplicating such configuration across your
|
||||
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 formatting is needed.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="format-configuring-FormatterRegistry">
|
||||
<section id="format-FormatterRegistrar-SPI">
|
||||
<title>FormatterRegistrar SPI</title>
|
||||
|
||||
<para> The FormatterRegistrar is an SPI for registering formatters and
|
||||
converters through the FormatterRegistry:
|
||||
</para>
|
||||
|
||||
<programlisting language="java"><![CDATA[package org.springframework.format;
|
||||
|
||||
public interface FormatterRegistrar {
|
||||
|
||||
void registerFormatters(FormatterRegistry registry);
|
||||
|
||||
}]]></programlisting>
|
||||
|
||||
<para> A FormatterRegistrar is useful when registering multiple related
|
||||
converters and formatters for a given formatting category, such as Date
|
||||
formatting. It can also be useful where declarative registration is
|
||||
insufficient. For example when a formatter needs to be indexed under a
|
||||
specific field type different from its own <T> or when registering
|
||||
a Printer/Parser pair. The next section provides more information on
|
||||
converter and formatter registration.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="format-configuring-FormattingConverionService">
|
||||
<title>Configuring Formatting in Spring MVC</title>
|
||||
|
||||
<para> In a Spring MVC application, you may configure a custom
|
||||
|
|
@ -1419,7 +1450,9 @@ public interface FormatterRegistry {
|
|||
classpath. </para>
|
||||
|
||||
<para> To inject a ConversionService instance with custom formatters and
|
||||
converters registered, set the conversion-service attribute: </para>
|
||||
converters registered, set the conversion-service attribute and then
|
||||
specify custom converters, formatters, or FormatterRegistrars as properties
|
||||
of the FormattingConversionServiceFactoryBean: </para>
|
||||
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||
|
|
@ -1433,15 +1466,35 @@ public interface FormatterRegistry {
|
|||
<mvc:annotation-driven conversion-service="conversionService"/>
|
||||
|
||||
<bean id="conversionService"
|
||||
class="org.springframework.format.support.FormattingConversionServiceFactoryBean"/>
|
||||
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
|
||||
<property name="converters">
|
||||
<set>
|
||||
<bean class="org.example.MyConverter"/>
|
||||
</set>
|
||||
</property>
|
||||
<property name="formatters">
|
||||
<set>
|
||||
<bean class="org.example.MyFormatter"/>
|
||||
<bean class="org.example.MyAnnotationFormatterFactory"/>
|
||||
</set>
|
||||
</property>
|
||||
<property name="formatterRegistrars">
|
||||
<set>
|
||||
<bean class="org.example.MyFormatterRegistrar"/>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
]]></programlisting>
|
||||
|
||||
<para> A custom ConversionService instance is often constructed by a
|
||||
FactoryBean that internally registers custom Formatters and Converters
|
||||
programatically before the ConversionService is returned. See
|
||||
FormattingConversionServiceFactoryBean for an example. </para>
|
||||
<note>
|
||||
<para> See <xref linkend="format-FormatterRegistrar-SPI"/> and
|
||||
the <classname>FormattingConversionServiceFactoryBean</classname>
|
||||
for more information on when to use FormatterRegistrars.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue