SPR-6375 - Register sensible default HTTP Message Converters based on what is available in the classpath

This commit is contained in:
Arjen Poutsma
2009-11-30 12:21:13 +00:00
parent 666700f7f3
commit 81d7f5bc5a
3 changed files with 117 additions and 54 deletions

View File

@@ -16,40 +16,56 @@
package org.springframework.web.servlet.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.util.ClassUtils;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
import org.w3c.dom.Element;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses the {@code annotation-driven} element to configure
* a Spring MVC web application.
* <p>
* Responsible for:
* {@link BeanDefinitionParser} that parses the {@code annotation-driven} element to configure a Spring MVC web
* application.
*
* <p>Responsible for:
* <ol>
* <li>Registering a DefaultAnnotationHandlerMapping bean for mapping HTTP Servlet Requests to @Controller methods using @RequestMapping annotations.
* <li>Registering a DefaultAnnotationHandlerMapping bean for mapping HTTP Servlet Requests to @Controller methods
* using @RequestMapping annotations.
* <li>Registering a AnnotationMethodHandlerAdapter bean for invoking annotated @Controller methods.
* Will configure the HandlerAdapter's <code>webBindingInitializer</code> property for centrally configuring @Controller DataBinder instances:
* Will configure the HandlerAdapter's <code>webBindingInitializer</code> property for centrally configuring
* {@code @Controller} {@code DataBinder} instances:
* <ul>
* <li>Configures the conversionService if specified, otherwise defaults to a fresh {@link ConversionService} instance created by the default {@link FormattingConversionServiceFactoryBean}.
* <li>Configures the validator if specified, otherwise defaults to a fresh {@link Validator} instance created by the default {@link LocalValidatorFactoryBean} <i>if the JSR-303 API is present in the classpath.
* <li>Configures the conversionService if specified, otherwise defaults to a fresh {@link ConversionService} instance
* created by the default {@link FormattingConversionServiceFactoryBean}.
* <li>Configures the validator if specified, otherwise defaults to a fresh {@link Validator} instance created by the
* default {@link LocalValidatorFactoryBean} <em>if the JSR-303 API is present on the classpath</em>.
* <li>Configures standard {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverters},
* including the {@link Jaxb2RootElementHttpMessageConverter} <em>if JAXB2 is present on the classpath</em>, and
* the {@link MappingJacksonHttpMessageConverter} <em>if Jackson is present on the classpath</em>.
* </ul>
* </ol>
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Arjen Poutsma
* @since 3.0
*/
public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
@@ -57,6 +73,14 @@ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParse
private static final boolean jsr303Present = ClassUtils.isPresent(
"javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private static final boolean jaxb2Present =
ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
private static final boolean jacksonPresent =
ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
@@ -74,6 +98,7 @@ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParse
RootBeanDefinition annAdapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class);
annAdapterDef.setSource(source);
annAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
annAdapterDef.getPropertyValues().add("messageConverters", getMessageConverters(source));
String adapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef);
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
@@ -113,4 +138,20 @@ public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParse
}
}
private ManagedList<RootBeanDefinition> getMessageConverters(Object source) {
ManagedList<RootBeanDefinition> messageConverters = new ManagedList<RootBeanDefinition>();
messageConverters.setSource(source);
messageConverters.add(new RootBeanDefinition(ByteArrayHttpMessageConverter.class));
messageConverters.add(new RootBeanDefinition(StringHttpMessageConverter.class));
messageConverters.add(new RootBeanDefinition(FormHttpMessageConverter.class));
messageConverters.add(new RootBeanDefinition(SourceHttpMessageConverter.class));
if (jaxb2Present) {
messageConverters.add(new RootBeanDefinition(Jaxb2RootElementHttpMessageConverter.class));
}
if (jacksonPresent) {
messageConverters.add(new RootBeanDefinition(MappingJacksonHttpMessageConverter.class));
}
return messageConverters;
}
}

View File

@@ -319,6 +319,14 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
this.customModelAndViewResolvers = customModelAndViewResolvers;
}
/**
* Returns the message body converters to use. These converters are used to convert from and to HTTP requests and
* responses.
*/
public HttpMessageConverter<?>[] getMessageConverters() {
return messageConverters;
}
/**
* Set the message body converters to use. These converters are used to convert from and to HTTP requests and
* responses.
@@ -605,7 +613,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer,
customArgumentResolvers, messageConverters);
customArgumentResolvers, getMessageConverters());
}
@Override
@@ -792,8 +800,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
Class<?> returnValueType = returnValue.getClass();
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
if (messageConverters != null) {
for (HttpMessageConverter messageConverter : messageConverters) {
if (getMessageConverters() != null) {
for (HttpMessageConverter messageConverter : getMessageConverters()) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
for (MediaType acceptedMediaType : acceptedMediaTypes) {
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {