diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java
index cdf3f5f8453..8417945de11 100644
--- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java
+++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -29,13 +29,13 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
- * Encapsulates information about a bean method consisting of a {@linkplain #getMethod() method} and a
- * {@linkplain #getBean() bean}. Provides convenient access to method parameters, the method return value,
+ * Encapsulates information about a bean method consisting of a {@linkplain #getMethod() method} and a
+ * {@linkplain #getBean() bean}. Provides convenient access to method parameters, the method return value,
* method annotations.
*
*
The class may be created with a bean instance or with a bean name (e.g. lazy bean, prototype bean).
* Use {@link #createWithResolvedBean()} to obtain an {@link HandlerMethod} instance with a bean instance
- * initialized through the bean factory.
+ * initialized through the bean factory.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
@@ -49,7 +49,7 @@ public class HandlerMethod {
private final Object bean;
private final Method method;
-
+
private final BeanFactory beanFactory;
private MethodParameter[] parameters;
@@ -87,7 +87,7 @@ public class HandlerMethod {
}
/**
- * Constructs a new handler method with the given bean name and method. The bean name will be lazily
+ * Constructs a new handler method with the given bean name and method. The bean name will be lazily
* initialized when {@link #createWithResolvedBean()} is called.
* @param beanName the bean name
* @param beanFactory the bean factory to use for bean initialization
@@ -120,7 +120,7 @@ public class HandlerMethod {
}
/**
- * Returns the type of the handler for this handler method.
+ * Returns the type of the handler for this handler method.
* Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned.
*/
public Class> getBeanType() {
@@ -132,7 +132,7 @@ public class HandlerMethod {
return ClassUtils.getUserClass(bean.getClass());
}
}
-
+
/**
* If the bean method is a bridge method, this method returns the bridged (user-defined) method.
* Otherwise it returns the same method as {@link #getMethod()}.
@@ -149,7 +149,7 @@ public class HandlerMethod {
int parameterCount = this.bridgedMethod.getParameterTypes().length;
MethodParameter[] p = new MethodParameter[parameterCount];
for (int i = 0; i < parameterCount; i++) {
- p[i] = new HandlerMethodParameter(this.bridgedMethod, i);
+ p[i] = new HandlerMethodParameter(i);
}
this.parameters = p;
}
@@ -157,10 +157,17 @@ public class HandlerMethod {
}
/**
- * Returns the method return type, as {@code MethodParameter}.
+ * Return the HandlerMethod return type.
*/
public MethodParameter getReturnType() {
- return new HandlerMethodParameter(this.bridgedMethod, -1);
+ return new HandlerMethodParameter(-1);
+ }
+
+ /**
+ * Return the actual return value type.
+ */
+ public MethodParameter getReturnValueType(Object returnValue) {
+ return new ReturnValueMethodParameter(returnValue);
}
/**
@@ -171,8 +178,8 @@ public class HandlerMethod {
}
/**
- * Returns a single annotation on the underlying method traversing its super methods if no
- * annotation can be found on the given method itself.
+ * Returns a single annotation on the underlying method traversing its super methods if no
+ * annotation can be found on the given method itself.
* @param annotationType the type of annotation to introspect the method for.
* @return the annotation, or {@code null} if none found
*/
@@ -181,7 +188,7 @@ public class HandlerMethod {
}
/**
- * If the provided instance contains a bean name rather than an object instance, the bean name is resolved
+ * If the provided instance contains a bean name rather than an object instance, the bean name is resolved
* before a {@link HandlerMethod} is created and returned.
*/
public HandlerMethod createWithResolvedBean() {
@@ -192,7 +199,7 @@ public class HandlerMethod {
}
return new HandlerMethod(handler, method);
}
-
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -216,33 +223,41 @@ public class HandlerMethod {
}
/**
- * A {@link MethodParameter} that resolves method annotations even when the actual annotations
- * are on a bridge method rather than on the current method. Annotations on super types are
- * also returned via {@link AnnotationUtils#findAnnotation(Method, Class)}.
+ * A MethodParameter with HandlerMethod-specific behavior.
*/
private class HandlerMethodParameter extends MethodParameter {
-
- public HandlerMethodParameter(Method method, int parameterIndex) {
- super(method, parameterIndex);
+
+ protected HandlerMethodParameter(int index) {
+ super(HandlerMethod.this.bridgedMethod, index);
}
- /**
- * Return {@link HandlerMethod#getBeanType()} rather than the method's class, which could be
- * important for the proper discovery of generic types.
- */
@Override
public Class> getDeclaringClass() {
return HandlerMethod.this.getBeanType();
}
- /**
- * Return the method annotation via {@link HandlerMethod#getMethodAnnotation(Class)}, which will find
- * the annotation by traversing super-types and handling annotations on bridge methods correctly.
- */
@Override
public T getMethodAnnotation(Class annotationType) {
return HandlerMethod.this.getMethodAnnotation(annotationType);
}
}
+ /**
+ * A MethodParameter for a HandlerMethod return type based on an actual return value.
+ */
+ private class ReturnValueMethodParameter extends HandlerMethodParameter {
+
+ private final Object returnValue;
+
+ public ReturnValueMethodParameter(Object returnValue) {
+ super(-1);
+ this.returnValue = returnValue;
+ }
+
+ @Override
+ public Class> getParameterType() {
+ return (this.returnValue != null) ? this.returnValue.getClass() : super.getParameterType();
+ }
+ }
+
}
diff --git a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java
index f06d458cbd9..fb364be2b39 100644
--- a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java
+++ b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java
@@ -19,8 +19,6 @@ package org.springframework.web.method.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -42,9 +40,6 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
private final List returnValueHandlers =
new ArrayList();
- private final Map returnValueHandlerCache =
- new ConcurrentHashMap();
-
/**
* Return a read-only list with the registered handlers, or an empty list.
*/
@@ -78,21 +73,16 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type.
*/
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
- HandlerMethodReturnValueHandler result = this.returnValueHandlerCache.get(returnType);
- if (result == null) {
- for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
- if (logger.isTraceEnabled()) {
- logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
- returnType.getGenericParameterType() + "]");
- }
- if (returnValueHandler.supportsReturnType(returnType)) {
- result = returnValueHandler;
- this.returnValueHandlerCache.put(returnType, returnValueHandler);
- break;
- }
+ for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
+ returnType.getGenericParameterType() + "]");
+ }
+ if (returnValueHandler.supportsReturnType(returnType)) {
+ return returnValueHandler;
}
}
- return result;
+ return null;
}
/**
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java
index ea92983720e..80b26b3794f 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 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.
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java
index 3eb3261fb7d..ea6ff054c0e 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java
@@ -107,7 +107,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
mavContainer.setRequestHandled(false);
try {
- returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
+ returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, request);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java
index 00a7a3abf57..2e1e62b0b2e 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -15,9 +15,10 @@
*/
package org.springframework.web.servlet.mvc.method.annotation;
-import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.lang.reflect.Method;
@@ -30,17 +31,20 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
+import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
+import org.springframework.web.servlet.view.RedirectView;
/**
* Test fixture with {@link ServletInvocableHandlerMethod}.
- *
+ *
* @author Rossen Stoyanchev
*/
public class ServletInvocableHandlerMethodTests {
@@ -53,6 +57,8 @@ public class ServletInvocableHandlerMethodTests {
private ServletWebRequest webRequest;
+ private MockHttpServletRequest request;
+
private MockHttpServletResponse response;
@Before
@@ -60,8 +66,9 @@ public class ServletInvocableHandlerMethodTests {
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
argumentResolvers = new HandlerMethodArgumentResolverComposite();
mavContainer = new ModelAndViewContainer();
+ request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
- webRequest = new ServletWebRequest(new MockHttpServletRequest(), response);
+ webRequest = new ServletWebRequest(request, response);
}
@Test
@@ -92,29 +99,45 @@ public class ServletInvocableHandlerMethodTests {
webRequest.getNativeRequest(MockHttpServletRequest.class).addHeader("If-Modified-Since", 10 * 1000 * 1000);
int lastModifiedTimestamp = 1000 * 1000;
webRequest.checkNotModified(lastModifiedTimestamp);
-
+
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("notModified");
handlerMethod.invokeAndHandle(webRequest, mavContainer);
assertTrue("Null return value + 'not modified' request should result in 'request handled'",
mavContainer.isRequestHandled());
}
-
- @Test
+
+ @Test(expected=HttpMessageNotWritableException.class)
public void exceptionWhileHandlingReturnValue() throws Exception {
returnValueHandlers.addHandler(new ExceptionRaisingReturnValueHandler());
ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("handle");
- try {
- handlerMethod.invokeAndHandle(webRequest, mavContainer);
- fail("Expected exception");
- } catch (HttpMessageNotWritableException ex) {
- // Expected..
- // Allow HandlerMethodArgumentResolver exceptions to propagate..
- }
+ handlerMethod.invokeAndHandle(webRequest, mavContainer);
+ fail("Expected exception");
}
- private ServletInvocableHandlerMethod getHandlerMethod(String methodName, Class>... argTypes)
+ @Test
+ public void dynamicReturnValue() throws Exception {
+ argumentResolvers.addResolver(new RequestParamMethodArgumentResolver(null, false));
+ returnValueHandlers.addHandler(new ViewMethodReturnValueHandler());
+ returnValueHandlers.addHandler(new ViewNameMethodReturnValueHandler());
+
+ // Invoke without a request parameter (String return value)
+ ServletInvocableHandlerMethod handlerMethod = getHandlerMethod("dynamicReturnValue", String.class);
+ handlerMethod.invokeAndHandle(webRequest, mavContainer);
+
+ assertNotNull(mavContainer.getView());
+ assertEquals(RedirectView.class, mavContainer.getView().getClass());
+
+ // Invoke with a request parameter (RedirectView return value)
+ request.setParameter("param", "value");
+ handlerMethod.invokeAndHandle(webRequest, mavContainer);
+
+ assertEquals("view", mavContainer.getViewName());
+ }
+
+
+ private ServletInvocableHandlerMethod getHandlerMethod(String methodName, Class>... argTypes)
throws NoSuchMethodException {
Method method = Handler.class.getDeclaredMethod(methodName, argTypes);
ServletInvocableHandlerMethod handlerMethod = new ServletInvocableHandlerMethod(new Handler(), method);
@@ -133,13 +156,16 @@ public class ServletInvocableHandlerMethodTests {
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "400 Bad Request")
public void responseStatus() {
}
-
+
public void httpServletResponse(HttpServletResponse response) {
}
-
+
public void notModified() {
}
-
+
+ public Object dynamicReturnValue(@RequestParam(required=false) String param) {
+ return (param != null) ? "view" : new RedirectView("redirectView");
+ }
}
private static class ExceptionRaisingReturnValueHandler implements HandlerMethodReturnValueHandler {
diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt
index da7d11a1c3a..928e2f52a2e 100644
--- a/src/dist/changelog.txt
+++ b/src/dist/changelog.txt
@@ -5,8 +5,11 @@ http://www.springsource.org
Changes in version 3.2 M1
-------------------------------------
-* fix issue with parsing invalid Content-Type or Accept headers
-
+* better handling on failure to parse invalid 'Content-Type' or 'Accept' headers
+* handle a controller method's return value based on the actual returned value (vs declared type)
+* fix issue with combining identical controller and method level request mapping paths
+* fix concurrency issue in AnnotationMethodHandlerExceptionResolver
+* fix case-sensitivity issue with some containers on access to 'Content-Disposition' header
Changes in version 3.1.1 (2012-02-16)
-------------------------------------