SPR-6093 - MVC Annotation Inheritance
This commit is contained in:
parent
d8245c800b
commit
04fa5d4b99
|
|
@ -341,7 +341,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
if (handler.getClass().getAnnotation(SessionAttributes.class) != null) {
|
if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {
|
||||||
// Always prevent caching in case of session attribute management.
|
// Always prevent caching in case of session attribute management.
|
||||||
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
|
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
|
||||||
// Prepare cached set of session attributes names.
|
// Prepare cached set of session attributes names.
|
||||||
|
|
@ -706,9 +706,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
Object returnValue,
|
Object returnValue,
|
||||||
ExtendedModelMap implicitModel,
|
ExtendedModelMap implicitModel,
|
||||||
ServletWebRequest webRequest) throws Exception {
|
ServletWebRequest webRequest) throws Exception {
|
||||||
|
|
||||||
if (handlerMethod.isAnnotationPresent(ResponseStatus.class)) {
|
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
|
||||||
ResponseStatus responseStatus = handlerMethod.getAnnotation(ResponseStatus.class);
|
if (responseStatus != null) {
|
||||||
HttpServletResponse response = webRequest.getResponse();
|
HttpServletResponse response = webRequest.getResponse();
|
||||||
response.setStatus(responseStatus.value().value());
|
response.setStatus(responseStatus.value().value());
|
||||||
responseArgumentUsed = true;
|
responseArgumentUsed = true;
|
||||||
|
|
@ -725,8 +725,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnValue != null && handlerMethod.isAnnotationPresent(ResponseBody.class)) {
|
if (returnValue != null && AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
|
||||||
handleRequestBody(returnValue, webRequest);
|
handleResponseBody(returnValue, webRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnValue instanceof ModelAndView) {
|
if (returnValue instanceof ModelAndView) {
|
||||||
|
|
@ -740,7 +740,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
else if (returnValue instanceof View) {
|
else if (returnValue instanceof View) {
|
||||||
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
|
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
|
||||||
}
|
}
|
||||||
else if (handlerMethod.isAnnotationPresent(ModelAttribute.class)) {
|
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
|
||||||
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
|
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
|
||||||
return new ModelAndView().addAllObjects(implicitModel);
|
return new ModelAndView().addAllObjects(implicitModel);
|
||||||
}
|
}
|
||||||
|
|
@ -771,7 +771,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void handleRequestBody(Object returnValue, ServletWebRequest webRequest) throws ServletException, IOException {
|
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws ServletException, IOException {
|
||||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
|
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
|
||||||
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
||||||
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
|
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
@ -56,8 +57,8 @@ import org.springframework.web.servlet.support.RequestContextUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver} interface that handles
|
* Implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver} interface that handles
|
||||||
* exceptions through the {@link ExceptionHandler} annotation.
|
* exceptions through the {@link ExceptionHandler} annotation. <p>This exception resolver is enabled by default in the
|
||||||
* <p>This exception resolver is enabled by default in the {@link org.springframework.web.servlet.DispatcherServlet}.
|
* {@link org.springframework.web.servlet.DispatcherServlet}.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
|
@ -110,7 +111,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
/**
|
/**
|
||||||
* Finds the handler method that matches the thrown exception best.
|
* Finds the handler method that matches the thrown exception best.
|
||||||
*
|
*
|
||||||
* @param handler the handler object
|
* @param handler the handler object
|
||||||
* @param thrownException the exception to be handled
|
* @param thrownException the exception to be handled
|
||||||
* @return the best matching method; or <code>null</code> if none is found
|
* @return the best matching method; or <code>null</code> if none is found
|
||||||
*/
|
*/
|
||||||
|
|
@ -132,23 +133,25 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Method oldMappedMethod = resolverMethods.get(handledException);
|
Method oldMappedMethod = resolverMethods.get(handledException);
|
||||||
throw new IllegalStateException(
|
if (!oldMappedMethod.equals(method)) {
|
||||||
"Ambiguous exception handler mapped for " + handledException + "]: {" +
|
throw new IllegalStateException(
|
||||||
oldMappedMethod + ", " + method + "}.");
|
"Ambiguous exception handler mapped for " + handledException + "]: {" +
|
||||||
|
oldMappedMethod + ", " + method + "}.");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return getBestMatchingMethod(thrownException, resolverMethods);
|
return getBestMatchingMethod(thrownException, resolverMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all the exception classes handled by the given method.
|
* Returns all the exception classes handled by the given method. <p>Default implementation looks for exceptions in the
|
||||||
* <p>Default implementation looks for exceptions in the {@linkplain ExceptionHandler#value() annotation}, or -
|
* {@linkplain ExceptionHandler#value() annotation}, or - if that annotation element is empty - any exceptions listed
|
||||||
* if that annotation element is empty - any exceptions listed in the method parameters if the method is annotated
|
* in the method parameters if the method is annotated with {@code @ExceptionHandler}.
|
||||||
* with {@code @ExceptionHandler}.
|
|
||||||
*
|
*
|
||||||
* @param method the method
|
* @param method the method
|
||||||
* @return the handled exceptions
|
* @return the handled exceptions
|
||||||
|
|
@ -156,7 +159,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected List<Class<? extends Throwable>> getHandledExceptions(Method method) {
|
protected List<Class<? extends Throwable>> getHandledExceptions(Method method) {
|
||||||
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
|
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
|
||||||
ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class);
|
ExceptionHandler exceptionHandler = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
|
||||||
if (exceptionHandler != null) {
|
if (exceptionHandler != null) {
|
||||||
if (!ObjectUtils.isEmpty(exceptionHandler.value())) {
|
if (!ObjectUtils.isEmpty(exceptionHandler.value())) {
|
||||||
result.addAll(Arrays.asList(exceptionHandler.value()));
|
result.addAll(Arrays.asList(exceptionHandler.value()));
|
||||||
|
|
@ -172,9 +175,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the best matching method. Uses the {@link DepthComparator}. */
|
||||||
* Returns the best matching method. Uses the {@link DepthComparator}.
|
|
||||||
*/
|
|
||||||
private Method getBestMatchingMethod(Exception thrownException,
|
private Method getBestMatchingMethod(Exception thrownException,
|
||||||
Map<Class<? extends Throwable>, Method> resolverMethods) {
|
Map<Class<? extends Throwable>, Method> resolverMethods) {
|
||||||
if (!resolverMethods.isEmpty()) {
|
if (!resolverMethods.isEmpty()) {
|
||||||
|
|
@ -189,9 +190,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Resolves the arguments for the given method. Delegates to {@link #resolveCommonArgument}. */
|
||||||
* Resolves the arguments for the given method. Delegates to {@link #resolveCommonArgument}.
|
|
||||||
*/
|
|
||||||
private Object[] resolveHandlerArguments(Method handlerMethod,
|
private Object[] resolveHandlerArguments(Method handlerMethod,
|
||||||
Object handler,
|
Object handler,
|
||||||
NativeWebRequest webRequest,
|
NativeWebRequest webRequest,
|
||||||
|
|
@ -222,11 +221,11 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves common method arguments. Delegates to registered {@link #setCustomArgumentResolver(WebArgumentResolver) argumentResolvers} first,
|
* Resolves common method arguments. Delegates to registered {@link #setCustomArgumentResolver(WebArgumentResolver)
|
||||||
* then checking {@link #resolveStandardArgument}.
|
* argumentResolvers} first, then checking {@link #resolveStandardArgument}.
|
||||||
*
|
*
|
||||||
* @param methodParameter the method parameter
|
* @param methodParameter the method parameter
|
||||||
* @param webRequest the request
|
* @param webRequest the request
|
||||||
* @param thrownException the exception thrown
|
* @param thrownException the exception thrown
|
||||||
* @return the argument value, or {@link WebArgumentResolver#UNRESOLVED}
|
* @return the argument value, or {@link WebArgumentResolver#UNRESOLVED}
|
||||||
*/
|
*/
|
||||||
|
|
@ -256,13 +255,13 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves standard method arguments. Default implementation handles {@link NativeWebRequest},
|
* Resolves standard method arguments. Default implementation handles {@link NativeWebRequest}, {@link ServletRequest},
|
||||||
* {@link ServletRequest}, {@link ServletResponse}, {@link HttpSession}, {@link Principal}, {@link Locale},
|
* {@link ServletResponse}, {@link HttpSession}, {@link Principal}, {@link Locale}, request {@link InputStream},
|
||||||
* request {@link InputStream}, request {@link Reader}, response {@link OutputStream}, response {@link Writer},
|
* request {@link Reader}, response {@link OutputStream}, response {@link Writer}, and the given {@code
|
||||||
* and the given {@code thrownException}.
|
* thrownException}.
|
||||||
*
|
*
|
||||||
* @param parameterType the method parameter type
|
* @param parameterType the method parameter type
|
||||||
* @param webRequest the request
|
* @param webRequest the request
|
||||||
* @param thrownException the exception thrown
|
* @param thrownException the exception thrown
|
||||||
* @return the argument value, or {@link WebArgumentResolver#UNRESOLVED}
|
* @return the argument value, or {@link WebArgumentResolver#UNRESOLVED}
|
||||||
*/
|
*/
|
||||||
|
|
@ -327,8 +326,8 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
private ModelAndView getModelAndView(Method handlerMethod, Object returnValue, ServletWebRequest webRequest)
|
private ModelAndView getModelAndView(Method handlerMethod, Object returnValue, ServletWebRequest webRequest)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
if (handlerMethod.isAnnotationPresent(ResponseStatus.class)) {
|
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
|
||||||
ResponseStatus responseStatus = handlerMethod.getAnnotation(ResponseStatus.class);
|
if (responseStatus != null) {
|
||||||
HttpServletResponse response = webRequest.getResponse();
|
HttpServletResponse response = webRequest.getResponse();
|
||||||
response.setStatus(responseStatus.value().value());
|
response.setStatus(responseStatus.value().value());
|
||||||
}
|
}
|
||||||
|
|
@ -355,9 +354,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Comparator capable of sorting exceptions based on their depth from the thrown exception type. */
|
||||||
* Comparator capable of sorting exceptions based on their depth from the thrown exception type.
|
|
||||||
*/
|
|
||||||
private static class DepthComparator implements Comparator<Class<? extends Throwable>> {
|
private static class DepthComparator implements Comparator<Class<? extends Throwable>> {
|
||||||
|
|
||||||
private final Class<? extends Throwable> handlerExceptionType;
|
private final Class<? extends Throwable> handlerExceptionType;
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,17 @@ public class AnnotationMethodHandlerExceptionResolverTests {
|
||||||
assertEquals("Invalid view name returned", "BindException", mav.getViewName());
|
assertEquals("Invalid view name returned", "BindException", mav.getViewName());
|
||||||
assertEquals("Invalid status code returned", 406, response.getStatus());
|
assertEquals("Invalid status code returned", 406, response.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inherited() {
|
||||||
|
IOException ex = new IOException();
|
||||||
|
InheritedController controller = new InheritedController();
|
||||||
|
ModelAndView mav = exceptionResolver.resolveException(request, response, controller, ex);
|
||||||
|
assertNotNull("No ModelAndView returned", mav);
|
||||||
|
assertEquals("Invalid view name returned", "GenericError", mav.getViewName());
|
||||||
|
assertEquals("Invalid status code returned", 500, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void ambiguous() {
|
public void ambiguous() {
|
||||||
IllegalArgumentException ex = new IllegalArgumentException();
|
IllegalArgumentException ex = new IllegalArgumentException();
|
||||||
|
|
@ -86,6 +96,7 @@ public class AnnotationMethodHandlerExceptionResolverTests {
|
||||||
private static class SimpleController {
|
private static class SimpleController {
|
||||||
|
|
||||||
@ExceptionHandler(IOException.class)
|
@ExceptionHandler(IOException.class)
|
||||||
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
public String handleIOException(IOException ex, HttpServletRequest request) {
|
public String handleIOException(IOException ex, HttpServletRequest request) {
|
||||||
return ClassUtils.getShortName(ex.getClass());
|
return ClassUtils.getShortName(ex.getClass());
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +114,15 @@ public class AnnotationMethodHandlerExceptionResolverTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
private static class InheritedController extends SimpleController
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public String handleIOException(IOException ex, HttpServletRequest request) {
|
||||||
|
return "GenericError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
private static class AmbiguousController {
|
private static class AmbiguousController {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue