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:sequence>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</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:all>
|
||||||
<xsd:attribute name="conversion-service" type="xsd:string">
|
<xsd:attribute name="conversion-service" type="xsd:string">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
|
|
|
||||||
|
|
@ -19,34 +19,16 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
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.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.DirectFieldAccessor;
|
import org.springframework.beans.DirectFieldAccessor;
|
||||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
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.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.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
import org.springframework.http.converter.ResourceHttpMessageConverter;
|
||||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
import org.springframework.validation.MessageCodesResolver;
|
import org.springframework.validation.MessageCodesResolver;
|
||||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
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.context.support.GenericWebApplicationContext;
|
||||||
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
|
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
|
||||||
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver;
|
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver;
|
||||||
|
|
@ -85,21 +67,6 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
||||||
verifyMessageConverters(appContext.getBean(AnnotationMethodHandlerExceptionResolver.class));
|
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) {
|
private void verifyMessageConverters(Object bean) {
|
||||||
assertNotNull(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.StringHttpMessageConverter"/>
|
||||||
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
|
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
|
||||||
</mvc:message-converters>
|
</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>
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
<bean id="messageCodesResolver"
|
<bean id="messageCodesResolver"
|
||||||
|
|
|
||||||
|
|
@ -1279,7 +1279,7 @@ public void handle(@RequestBody String body, Writer writer) throws IOException {
|
||||||
the <classname>AnnotationMethodHandlerAdapter</classname> is extended
|
the <classname>AnnotationMethodHandlerAdapter</classname> is extended
|
||||||
to support the <classname>@RequestBody</classname> and has the
|
to support the <classname>@RequestBody</classname> and has the
|
||||||
following <interfacename>HttpMessageConverters</interfacename>
|
following <interfacename>HttpMessageConverters</interfacename>
|
||||||
registered by default:</para>
|
registered by default if not using the MVC namespace:</para>
|
||||||
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
@ -1301,16 +1301,12 @@ public void handle(@RequestBody String body, Writer writer) throws IOException {
|
||||||
<para><classname>SourceHttpMessageConverter</classname> converts
|
<para><classname>SourceHttpMessageConverter</classname> converts
|
||||||
to/from a javax.xml.transform.Source.</para>
|
to/from a javax.xml.transform.Source.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
<listitem>
|
|
||||||
<para><classname>MarshallingHttpMessageConverter</classname>
|
|
||||||
converts to/from an object using the
|
|
||||||
<classname>org.springframework.oxm</classname> package.</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
<para>For more information on these converters, see <link
|
<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>
|
<para>The <classname>MarshallingHttpMessageConverter</classname>
|
||||||
requires a <interfacename>Marshaller</interfacename> and
|
requires a <interfacename>Marshaller</interfacename> and
|
||||||
|
|
@ -3313,13 +3309,69 @@ public class SimpleController {
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<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>
|
</para>
|
||||||
</listitem>
|
|
||||||
<listitem>
|
|
||||||
<para>
|
<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>
|
</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>
|
</listitem>
|
||||||
</orderedlist>
|
</orderedlist>
|
||||||
A typical usage is shown below:
|
A typical usage is shown below:
|
||||||
|
|
|
||||||
|
|
@ -172,17 +172,23 @@
|
||||||
|
|
||||||
<surname>Gierke</surname>
|
<surname>Gierke</surname>
|
||||||
</author>
|
</author>
|
||||||
|
|
||||||
|
<author>
|
||||||
|
<firstname>Rossen</firstname>
|
||||||
|
<surname>Stoyanchev</surname>
|
||||||
|
</author>
|
||||||
|
|
||||||
</authorgroup>
|
</authorgroup>
|
||||||
|
|
||||||
<copyright>
|
<copyright>
|
||||||
<year>2004-2010</year>
|
<year>2004-2011</year>
|
||||||
|
|
||||||
<holder>Rod Johnson, Juergen Hoeller, Keith Donald, Colin Sampaleanu,
|
<holder>Rod Johnson, Juergen Hoeller, Keith Donald, Colin Sampaleanu,
|
||||||
Rob Harrop, Alef Arendsen, Thomas Risberg, Darren Davison, Dmitriy
|
Rob Harrop, Alef Arendsen, Thomas Risberg, Darren Davison, Dmitriy
|
||||||
Kopylenko, Mark Pollack, Thierry Templier, Erwin Vervaet, Portia Tung,
|
Kopylenko, Mark Pollack, Thierry Templier, Erwin Vervaet, Portia Tung,
|
||||||
Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
|
Ben Hale, Adrian Colyer, John Lewis, Costin Leau, Mark Fisher, Sam
|
||||||
Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo,
|
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>
|
</copyright>
|
||||||
|
|
||||||
<legalnotice>
|
<legalnotice>
|
||||||
|
|
|
||||||
|
|
@ -1350,41 +1350,72 @@ public interface AnnotationFormatterFactory<A extends Annotation> {
|
||||||
<section id="format-FormatterRegistry-SPI">
|
<section id="format-FormatterRegistry-SPI">
|
||||||
<title>FormatterRegistry SPI</title>
|
<title>FormatterRegistry SPI</title>
|
||||||
|
|
||||||
<para> At runtime, Formatters are registered in a FormatterRegistry. The
|
<para> The FormatterRegistry is an SPI for registering formatters and
|
||||||
FormatterRegistry SPI allows you to configure Formatting rules
|
converters. <classname>FormattingConversionService</classname> is
|
||||||
centrally, instead of duplicating such configuration across your
|
an implementation of FormatterRegistry suitable for most environments.
|
||||||
Controllers. For example, you might want to enforce that all Date fields
|
This implementation may be configured programatically or declaratively
|
||||||
are formatted a certain way, or fields with a specific annotation are
|
as a Spring bean using
|
||||||
formatted in a certain way. With a shared FormatterRegistry, you define
|
<classname>FormattingConversionServiceFactoryBean</classname>.
|
||||||
these rules once and they are applied whenever formatting is needed. </para>
|
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>
|
<para> Review the FormatterRegistry SPI below: </para>
|
||||||
|
|
||||||
<programlisting language="java"><![CDATA[package org.springframework.format;
|
<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, Printer<?> printer, Parser<?> parser);
|
||||||
|
|
||||||
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
|
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
|
||||||
|
|
||||||
|
void addFormatterForFieldType(Formatter<?> formatter);
|
||||||
|
|
||||||
void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory);
|
void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory);
|
||||||
|
|
||||||
}]]></programlisting>
|
}]]></programlisting>
|
||||||
|
|
||||||
<para> As shown above, Formatters can be registered by fieldType or
|
<para> As shown above, Formatters can be registered by fieldType or
|
||||||
annotation. <classname>FormattingConversionService</classname> is the
|
annotation.
|
||||||
implementation of <classname>FormatterRegistry</classname> suitable for
|
</para>
|
||||||
most environments. This implementation may be configured
|
<para> The FormatterRegistry SPI allows you to configure Formatting rules
|
||||||
programatically, or declaratively as a Spring bean using
|
centrally, instead of duplicating such configuration across your
|
||||||
<classname>FormattingConversionServiceFactoryBean</classname>. Because
|
Controllers. For example, you might want to enforce that all Date fields
|
||||||
this implemementation also implements
|
are formatted a certain way, or fields with a specific annotation are
|
||||||
<classname>ConversionService</classname>, it can be directly configured
|
formatted in a certain way. With a shared FormatterRegistry, you define
|
||||||
for use with Spring's DataBinder and the Spring Expression Language
|
these rules once and they are applied whenever formatting is needed.
|
||||||
(SpEL). </para>
|
</para>
|
||||||
</section>
|
</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>
|
<title>Configuring Formatting in Spring MVC</title>
|
||||||
|
|
||||||
<para> In a Spring MVC application, you may configure a custom
|
<para> In a Spring MVC application, you may configure a custom
|
||||||
|
|
@ -1419,7 +1450,9 @@ public interface FormatterRegistry {
|
||||||
classpath. </para>
|
classpath. </para>
|
||||||
|
|
||||||
<para> To inject a ConversionService instance with custom formatters and
|
<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"?>
|
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||||
|
|
@ -1433,15 +1466,35 @@ public interface FormatterRegistry {
|
||||||
<mvc:annotation-driven conversion-service="conversionService"/>
|
<mvc:annotation-driven conversion-service="conversionService"/>
|
||||||
|
|
||||||
<bean id="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>
|
</beans>
|
||||||
]]></programlisting>
|
]]></programlisting>
|
||||||
|
|
||||||
<para> A custom ConversionService instance is often constructed by a
|
<note>
|
||||||
FactoryBean that internally registers custom Formatters and Converters
|
<para> See <xref linkend="format-FormatterRegistrar-SPI"/> and
|
||||||
programatically before the ConversionService is returned. See
|
the <classname>FormattingConversionServiceFactoryBean</classname>
|
||||||
FormattingConversionServiceFactoryBean for an example. </para>
|
for more information on when to use FormatterRegistrars.
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue