SPR-7732, SPR-6506, SPR-7191 MVC Namespace improvements to the annotation-driven element - custom message converters, formatters, and message codes resolver.
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3920 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
3145ebc0a2
commit
174f609be4
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2009 the original author or authors.
|
* Copyright 2002-2011 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -30,7 +30,7 @@ import org.springframework.core.convert.converter.ConverterRegistry;
|
||||||
public interface FormatterRegistry extends ConverterRegistry {
|
public interface FormatterRegistry extends ConverterRegistry {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a Formatter to format fields of a specific type.
|
* Adds a Formatter to format fields of the given type.
|
||||||
* <p>On print, if the Formatter's type T is declared and <code>fieldType</code> is not assignable to T,
|
* <p>On print, if the Formatter's type T is declared and <code>fieldType</code> is not assignable to T,
|
||||||
* a coersion to T will be attempted before delegating to <code>formatter</code> to print a field value.
|
* a coersion to T will be attempted before delegating to <code>formatter</code> to print a field value.
|
||||||
* On parse, if the parsed object returned by <code>formatter</code> is not assignable to the runtime field type,
|
* On parse, if the parsed object returned by <code>formatter</code> is not assignable to the runtime field type,
|
||||||
|
|
@ -40,6 +40,14 @@ public interface FormatterRegistry extends ConverterRegistry {
|
||||||
*/
|
*/
|
||||||
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
|
void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a Formatter to format fields of a specific type.
|
||||||
|
* The field type is implied by the parameterized Formatter instance.
|
||||||
|
* @param formatter the formatter to add
|
||||||
|
* @see #addFormatterForFieldType(Class, Formatter)
|
||||||
|
*/
|
||||||
|
void addFormatter(Formatter<?> formatter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a Printer/Parser pair to format fields of a specific type.
|
* Adds a Printer/Parser pair to format fields of a specific type.
|
||||||
* The formatter will delegate to the specified <code>printer</code> for printing
|
* The formatter will delegate to the specified <code>printer</code> for printing
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2010 the original author or authors.
|
* Copyright 2002-2011 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -68,12 +68,21 @@ public class FormattingConversionService extends GenericConversionService
|
||||||
addConverter(new ParserConverter(fieldType, formatter, this));
|
addConverter(new ParserConverter(fieldType, formatter, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addFormatter(Formatter<?> formatter) {
|
||||||
|
final Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||||
|
if (fieldType == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter ["
|
||||||
|
+ formatter.getClass().getName() + "]; does the formatter parameterize the <T> generic type?");
|
||||||
|
}
|
||||||
|
addFormatterForFieldType(fieldType, formatter);
|
||||||
|
}
|
||||||
|
|
||||||
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
|
public void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser) {
|
||||||
addConverter(new PrinterConverter(fieldType, printer, this));
|
addConverter(new PrinterConverter(fieldType, printer, this));
|
||||||
addConverter(new ParserConverter(fieldType, parser, this));
|
addConverter(new ParserConverter(fieldType, parser, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
public void addFormatterForFieldAnnotation(final AnnotationFormatterFactory annotationFormatterFactory) {
|
public void addFormatterForFieldAnnotation(final AnnotationFormatterFactory annotationFormatterFactory) {
|
||||||
final Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
|
final Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
|
||||||
GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
|
GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
|
||||||
|
|
@ -174,7 +183,7 @@ public class FormattingConversionService extends GenericConversionService
|
||||||
|
|
||||||
private TypeDescriptor printerObjectType;
|
private TypeDescriptor printerObjectType;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("rawtypes")
|
||||||
private Printer printer;
|
private Printer printer;
|
||||||
|
|
||||||
private ConversionService conversionService;
|
private ConversionService conversionService;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2010 the original author or authors.
|
* Copyright 2002-2011 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -27,6 +27,7 @@ import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.context.EmbeddedValueResolverAware;
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
import org.springframework.core.convert.support.ConversionServiceFactory;
|
import org.springframework.core.convert.support.ConversionServiceFactory;
|
||||||
import org.springframework.format.AnnotationFormatterFactory;
|
import org.springframework.format.AnnotationFormatterFactory;
|
||||||
|
import org.springframework.format.Formatter;
|
||||||
import org.springframework.format.FormatterRegistry;
|
import org.springframework.format.FormatterRegistry;
|
||||||
import org.springframework.format.Parser;
|
import org.springframework.format.Parser;
|
||||||
import org.springframework.format.Printer;
|
import org.springframework.format.Printer;
|
||||||
|
|
@ -38,10 +39,8 @@ import org.springframework.util.StringValueResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory for a {@link FormattingConversionService} that installs default
|
* A factory for a {@link FormattingConversionService} that installs default
|
||||||
* formatters for common types such as numbers and datetimes.
|
* and custom converters and formatters for common types such as numbers
|
||||||
*
|
* and datetimes .
|
||||||
* <p>Subclasses may override {@link #installFormatters(FormatterRegistry)}
|
|
||||||
* to register custom formatters.
|
|
||||||
*
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
|
@ -55,21 +54,31 @@ public class FormattingConversionServiceFactoryBean
|
||||||
|
|
||||||
private Set<?> converters;
|
private Set<?> converters;
|
||||||
|
|
||||||
|
private Set<?> formatters;
|
||||||
|
|
||||||
private StringValueResolver embeddedValueResolver;
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
private FormattingConversionService conversionService;
|
private FormattingConversionService conversionService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the set of custom converter objects that should be added:
|
* Configure the set of custom converter objects that should be added.
|
||||||
* implementing {@link org.springframework.core.convert.converter.Converter},
|
* @param converters instances of
|
||||||
* {@link org.springframework.core.convert.converter.ConverterFactory},
|
* {@link org.springframework.core.convert.converter.Converter},
|
||||||
* or {@link org.springframework.core.convert.converter.GenericConverter}.
|
* {@link org.springframework.core.convert.converter.ConverterFactory} or
|
||||||
|
* {@link org.springframework.core.convert.converter.GenericConverter}.
|
||||||
*/
|
*/
|
||||||
public void setConverters(Set<?> converters) {
|
public void setConverters(Set<?> converters) {
|
||||||
this.converters = converters;
|
this.converters = converters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the set of custom formatter objects that should be added.
|
||||||
|
* @param formatters instances of {@link Formatter} or {@link AnnotationFormatterFactory}.
|
||||||
|
*/
|
||||||
|
public void setFormatters(Set<?> formatters) {
|
||||||
|
this.formatters = formatters;
|
||||||
|
}
|
||||||
|
|
||||||
public void setEmbeddedValueResolver(StringValueResolver embeddedValueResolver) {
|
public void setEmbeddedValueResolver(StringValueResolver embeddedValueResolver) {
|
||||||
this.embeddedValueResolver = embeddedValueResolver;
|
this.embeddedValueResolver = embeddedValueResolver;
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +121,18 @@ public class FormattingConversionServiceFactoryBean
|
||||||
else {
|
else {
|
||||||
registry.addFormatterForFieldAnnotation(new NoJodaDateTimeFormatAnnotationFormatterFactory());
|
registry.addFormatterForFieldAnnotation(new NoJodaDateTimeFormatAnnotationFormatterFactory());
|
||||||
}
|
}
|
||||||
|
if (this.formatters != null) {
|
||||||
|
for (Object formatter : this.formatters) {
|
||||||
|
if (formatter instanceof Formatter<?>) {
|
||||||
|
this.conversionService.addFormatter((Formatter<?>) formatter);
|
||||||
|
} else if (formatter instanceof AnnotationFormatterFactory<?>) {
|
||||||
|
this.conversionService.addFormatterForFieldAnnotation((AnnotationFormatterFactory<?>) formatter);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Custom formatters must be implementations of Formatter or AnnotationFormatterFactory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.web.servlet.config;
|
package org.springframework.web.servlet.config;
|
||||||
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||||
|
|
@ -39,6 +38,7 @@ import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConvert
|
||||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||||
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.xml.DomUtils;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||||
|
|
@ -49,6 +49,7 @@ import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExc
|
||||||
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
|
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
|
||||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanDefinitionParser} that parses the {@code annotation-driven} element to configure a Spring MVC web
|
* {@link BeanDefinitionParser} that parses the {@code annotation-driven} element to configure a Spring MVC web
|
||||||
|
|
@ -75,6 +76,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
@ -107,14 +109,16 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
||||||
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
|
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
|
||||||
RuntimeBeanReference validator = getValidator(element, source, parserContext);
|
RuntimeBeanReference validator = getValidator(element, source, parserContext);
|
||||||
|
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element, source, parserContext);
|
||||||
|
|
||||||
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
|
||||||
bindingDef.setSource(source);
|
bindingDef.setSource(source);
|
||||||
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
bindingDef.getPropertyValues().add("conversionService", conversionService);
|
bindingDef.getPropertyValues().add("conversionService", conversionService);
|
||||||
bindingDef.getPropertyValues().add("validator", validator);
|
bindingDef.getPropertyValues().add("validator", validator);
|
||||||
|
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
|
||||||
|
|
||||||
ManagedList<RootBeanDefinition> messageConverters = getMessageConverters(source);
|
ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
|
||||||
|
|
||||||
RootBeanDefinition annAdapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
RootBeanDefinition annAdapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
|
||||||
annAdapterDef.setSource(source);
|
annAdapterDef.setSource(source);
|
||||||
|
|
@ -166,10 +170,10 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
|
private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) {
|
||||||
|
RuntimeBeanReference conversionServiceRef;
|
||||||
if (element.hasAttribute("conversion-service")) {
|
if (element.hasAttribute("conversion-service")) {
|
||||||
return new RuntimeBeanReference(element.getAttribute("conversion-service"));
|
conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
|
RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);
|
||||||
|
|
@ -177,8 +181,21 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
conversionDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
|
String conversionName = parserContext.getReaderContext().registerWithGeneratedName(conversionDef);
|
||||||
parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
|
parserContext.registerComponent(new BeanComponentDefinition(conversionDef, conversionName));
|
||||||
return new RuntimeBeanReference(conversionName);
|
conversionServiceRef = new RuntimeBeanReference(conversionName);
|
||||||
}
|
}
|
||||||
|
Element formattersElement = DomUtils.getChildElementByTagName(element, "formatters");
|
||||||
|
if (formattersElement != null) {
|
||||||
|
ManagedList<BeanDefinitionHolder> formatters = new ManagedList<BeanDefinitionHolder>();
|
||||||
|
formatters.setSource(source);
|
||||||
|
for (Element formatter : DomUtils.getChildElementsByTagName(formattersElement, "bean")) {
|
||||||
|
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(formatter);
|
||||||
|
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(formatter, beanDef);
|
||||||
|
formatters.add(beanDef);
|
||||||
|
}
|
||||||
|
BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(conversionServiceRef.getBeanName());
|
||||||
|
beanDef.getPropertyValues().add("formatters", formatters);
|
||||||
|
}
|
||||||
|
return conversionServiceRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
|
private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) {
|
||||||
|
|
@ -198,29 +215,51 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManagedList<RootBeanDefinition> getMessageConverters(Object source) {
|
private RuntimeBeanReference getMessageCodesResolver(Element element, Object source, ParserContext parserContext) {
|
||||||
ManagedList<RootBeanDefinition> messageConverters = new ManagedList<RootBeanDefinition>();
|
if (element.hasAttribute("message-codes-resolver")) {
|
||||||
messageConverters.setSource(source);
|
return new RuntimeBeanReference(element.getAttribute("message-codes-resolver"));
|
||||||
messageConverters.add(createConverterBeanDefinition(ByteArrayHttpMessageConverter.class, source));
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RootBeanDefinition stringConverterDef = createConverterBeanDefinition(StringHttpMessageConverter.class, source);
|
private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {
|
||||||
stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
|
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
|
||||||
messageConverters.add(stringConverterDef);
|
if (convertersElement != null) {
|
||||||
|
ManagedList<BeanDefinitionHolder> messageConverters = new ManagedList<BeanDefinitionHolder>();
|
||||||
|
messageConverters.setSource(source);
|
||||||
|
for (Element converter : DomUtils.getChildElementsByTagName(convertersElement, "bean")) {
|
||||||
|
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(converter);
|
||||||
|
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(converter, beanDef);
|
||||||
|
messageConverters.add(beanDef);
|
||||||
|
}
|
||||||
|
return messageConverters;
|
||||||
|
} else {
|
||||||
|
ManagedList<RootBeanDefinition> messageConverters = new ManagedList<RootBeanDefinition>();
|
||||||
|
messageConverters.setSource(source);
|
||||||
|
messageConverters.add(createConverterBeanDefinition(ByteArrayHttpMessageConverter.class, source));
|
||||||
|
|
||||||
messageConverters.add(createConverterBeanDefinition(ResourceHttpMessageConverter.class, source));
|
RootBeanDefinition stringConverterDef = createConverterBeanDefinition(StringHttpMessageConverter.class,
|
||||||
messageConverters.add(createConverterBeanDefinition(SourceHttpMessageConverter.class, source));
|
source);
|
||||||
messageConverters.add(createConverterBeanDefinition(XmlAwareFormHttpMessageConverter.class, source));
|
stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
|
||||||
if (jaxb2Present) {
|
messageConverters.add(stringConverterDef);
|
||||||
messageConverters.add(createConverterBeanDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
|
|
||||||
|
messageConverters.add(createConverterBeanDefinition(ResourceHttpMessageConverter.class, source));
|
||||||
|
messageConverters.add(createConverterBeanDefinition(SourceHttpMessageConverter.class, source));
|
||||||
|
messageConverters.add(createConverterBeanDefinition(XmlAwareFormHttpMessageConverter.class, source));
|
||||||
|
if (jaxb2Present) {
|
||||||
|
messageConverters
|
||||||
|
.add(createConverterBeanDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
|
||||||
|
}
|
||||||
|
if (jacksonPresent) {
|
||||||
|
messageConverters.add(createConverterBeanDefinition(MappingJacksonHttpMessageConverter.class, source));
|
||||||
|
}
|
||||||
|
if (romePresent) {
|
||||||
|
messageConverters.add(createConverterBeanDefinition(AtomFeedHttpMessageConverter.class, source));
|
||||||
|
messageConverters.add(createConverterBeanDefinition(RssChannelHttpMessageConverter.class, source));
|
||||||
|
}
|
||||||
|
return messageConverters;
|
||||||
}
|
}
|
||||||
if (jacksonPresent) {
|
|
||||||
messageConverters.add(createConverterBeanDefinition(MappingJacksonHttpMessageConverter.class, source));
|
|
||||||
}
|
|
||||||
if (romePresent) {
|
|
||||||
messageConverters.add(createConverterBeanDefinition(AtomFeedHttpMessageConverter.class, source));
|
|
||||||
messageConverters.add(createConverterBeanDefinition(RssChannelHttpMessageConverter.class, source));
|
|
||||||
}
|
|
||||||
return messageConverters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RootBeanDefinition createConverterBeanDefinition(Class<? extends HttpMessageConverter> converterClass,
|
private RootBeanDefinition createConverterBeanDefinition(Class<? extends HttpMessageConverter> converterClass,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<xsd:schema xmlns="http://www.springframework.org/schema/mvc"
|
<xsd:schema xmlns="http://www.springframework.org/schema/mvc"
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||||
targetNamespace="http://www.springframework.org/schema/mvc"
|
targetNamespace="http://www.springframework.org/schema/mvc"
|
||||||
elementFormDefault="qualified" attributeFormDefault="unqualified">
|
elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||||
|
|
@ -16,6 +17,47 @@
|
||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
|
<xsd:all minOccurs="0">
|
||||||
|
<xsd:element name="message-converters">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Configures one or more HttpMessageConverter types to use for converting @RequestBody method parameters and @ResponseBody method return values.
|
||||||
|
Using this configuration element is optional.
|
||||||
|
If used it overrides the default set of HttpMessageConverter type registrations.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element ref="beans:bean" minOccurs="1" maxOccurs="unbounded">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The HttpMessageConverter bean definition.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
</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:attribute name="conversion-service" type="xsd:string">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation source="java:org.springframework.core.convert.ConversionService"><![CDATA[
|
<xsd:documentation source="java:org.springframework.core.convert.ConversionService"><![CDATA[
|
||||||
|
|
@ -45,6 +87,20 @@
|
||||||
</xsd:appinfo>
|
</xsd:appinfo>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="message-codes-resolver" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The bean name of a MessageCodesResolver to use to build message codes from data binding and validation error codes.
|
||||||
|
This attribute is not required.
|
||||||
|
If not specified the DefaultMessageCodesResolver is used.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation kind="ref">
|
||||||
|
<tool:expected-type type="java:org.springframework.validation.MessageCodesResolver" />
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
|
|
||||||
|
|
@ -161,11 +217,11 @@
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element ref="beans:bean">
|
<xsd:element ref="beans:bean">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation><![CDATA[
|
<xsd:documentation><![CDATA[
|
||||||
The interceptor's bean definition.
|
The interceptor's bean definition.
|
||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* 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.web.servlet.config;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test fixture for the configuration in mvc-config-annotation-driven.xml.
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
|
*/
|
||||||
|
public class AnnotationDrivenBeanDefinitionParserTests {
|
||||||
|
|
||||||
|
private static GenericWebApplicationContext appContext;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setup() {
|
||||||
|
appContext = new GenericWebApplicationContext();
|
||||||
|
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext);
|
||||||
|
reader.loadBeanDefinitions(new ClassPathResource("mvc-config-annotation-driven.xml",
|
||||||
|
AnnotationDrivenBeanDefinitionParserTests.class));
|
||||||
|
appContext.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMessageCodesResolver() {
|
||||||
|
AnnotationMethodHandlerAdapter adapter = appContext.getBean(AnnotationMethodHandlerAdapter.class);
|
||||||
|
assertNotNull(adapter);
|
||||||
|
Object initializer = new DirectFieldAccessor(adapter).getPropertyValue("webBindingInitializer");
|
||||||
|
assertNotNull(initializer);
|
||||||
|
MessageCodesResolver resolver = ((ConfigurableWebBindingInitializer) initializer).getMessageCodesResolver();
|
||||||
|
assertNotNull(resolver);
|
||||||
|
assertEquals(TestMessageCodesResolver.class, resolver.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMessageConverters() {
|
||||||
|
verifyMessageConverters(appContext.getBean(AnnotationMethodHandlerAdapter.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) {
|
||||||
|
assertNotNull(bean);
|
||||||
|
Object converters = new DirectFieldAccessor(bean).getPropertyValue("messageConverters");
|
||||||
|
assertNotNull(converters);
|
||||||
|
assertTrue(converters instanceof HttpMessageConverter<?>[]);
|
||||||
|
assertEquals(2, ((HttpMessageConverter<?>[]) converters).length);
|
||||||
|
assertTrue(((HttpMessageConverter<?>[]) converters)[0] instanceof StringHttpMessageConverter);
|
||||||
|
assertTrue(((HttpMessageConverter<?>[]) converters)[1] instanceof ResourceHttpMessageConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestMessageCodesResolver implements MessageCodesResolver {
|
||||||
|
|
||||||
|
public String[] resolveMessageCodes(String errorCode, String objectName) {
|
||||||
|
throw new IllegalStateException("Not expected to be invoked");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public String[] resolveMessageCodes(String errorCode, String objectName, String field, Class fieldType) {
|
||||||
|
throw new IllegalStateException("Not expected to be invoked");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
|
||||||
|
|
||||||
|
<mvc:annotation-driven message-codes-resolver="messageCodesResolver">
|
||||||
|
<mvc:message-converters>
|
||||||
|
<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"
|
||||||
|
class="org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParserTests$TestMessageCodesResolver"/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
Loading…
Reference in New Issue