Autowiring of generic types

Includes revisions of MethodParameter and DependencyDescriptor (in particular towards a reference to the containing class). Also refines several ResolvableType method signatures.

Issue: SPR-9965
This commit is contained in:
Juergen Hoeller
2013-10-15 00:11:23 +02:00
parent 74794190a5
commit 3bd6dfe047
19 changed files with 874 additions and 351 deletions

View File

@@ -23,11 +23,11 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpInputMessage;
@@ -56,12 +56,14 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
protected final List<MediaType> allSupportedMediaTypes;
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
this.messageConverters = messageConverters;
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
}
/**
* Return the media types supported by all provided message converters sorted
* by specificity via {@link MediaType#sortBySpecificity(List)}.
@@ -77,9 +79,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
}
/**
* Creates the method argument value of the expected parameter type by
* Create the method argument value of the expected parameter type by
* reading from the given request.
*
* @param <T> the expected type of the argument value to be created
* @param webRequest the current request
* @param methodParam the method argument
@@ -96,9 +97,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
}
/**
* Creates the method argument value of the expected parameter type by reading
* Create the method argument value of the expected parameter type by reading
* from the given HttpInputMessage.
*
* @param <T> the expected type of the argument value to be created
* @param inputMessage the HTTP input message representing the current request
* @param methodParam the method argument
@@ -113,43 +113,42 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
MediaType contentType = inputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
MediaType contentType = inputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = methodParam.getDeclaringClass();
Class<T> targetClass = (Class<T>) ResolvableType.forType(targetType,
ResolvableType.forMethodParameter(methodParam)).resolve();
Class<?> contextClass = methodParam.getContainingClass();
Class<T> targetClass = (Class<T>) ResolvableType.forType(targetType,
ResolvableType.forMethodParameter(methodParam)).resolve();
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetType + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return genericConverter.read(targetType, contextClass, inputMessage);
}
}
if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
}
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetType + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return genericConverter.read(targetType, contextClass, inputMessage);
}
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
}
if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
}
}
}
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
}
/**
* Creates a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
*
* Create a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
* @param webRequest the web request to create an input message from
* @return the input message
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -80,7 +80,7 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
if (this.mavResolvers != null) {
for (ModelAndViewResolver mavResolver : this.mavResolvers) {
Class<?> handlerType = returnType.getDeclaringClass();
Class<?> handlerType = returnType.getContainingClass();
Method method = returnType.getMethod();
ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel();
ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, request);
@@ -95,14 +95,13 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
}
}
// No suitable ModelAndViewResolver..
// No suitable ModelAndViewResolver...
if (this.modelAttributeProcessor.supportsReturnType(returnType)) {
this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, request);
}
else {
throw new UnsupportedOperationException("Unexpected return type: "
+ returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -22,7 +22,6 @@ import java.io.PushbackInputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.Conventions;
@@ -81,16 +80,16 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ((AnnotationUtils.findAnnotation(returnType.getDeclaringClass(), ResponseBody.class) != null)
|| (returnType.getMethodAnnotation(ResponseBody.class) != null));
return ((AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null) ||
(returnType.getMethodAnnotation(ResponseBody.class) != null));
}
/**
* {@inheritDoc}
* @throws MethodArgumentNotValidException if validation fails
* @throws HttpMessageNotReadableException if {@link RequestBody#required()}
* is {@code true} and there is no body content or if there is no suitable
* converter to read the content with.
* is {@code true} and there is no body content or if there is no suitable
* converter to read the content with.
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,