From 04fa5d4b99989d96a478fd788b0e821aba9f32d6 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 25 Sep 2009 08:45:58 +0000 Subject: [PATCH] SPR-6093 - MVC Annotation Inheritance --- .../AnnotationMethodHandlerAdapter.java | 16 ++--- ...otationMethodHandlerExceptionResolver.java | 59 +++++++++---------- ...onMethodHandlerExceptionResolverTests.java | 22 ++++++- 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index 349d263c4c..17ccb6a312 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -341,7 +341,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) 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. checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true); // Prepare cached set of session attributes names. @@ -706,9 +706,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen Object returnValue, ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception { - - if (handlerMethod.isAnnotationPresent(ResponseStatus.class)) { - ResponseStatus responseStatus = handlerMethod.getAnnotation(ResponseStatus.class); + + ResponseStatus responseStatus = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class); + if (responseStatus != null) { HttpServletResponse response = webRequest.getResponse(); response.setStatus(responseStatus.value().value()); responseArgumentUsed = true; @@ -725,8 +725,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } - if (returnValue != null && handlerMethod.isAnnotationPresent(ResponseBody.class)) { - handleRequestBody(returnValue, webRequest); + if (returnValue != null && AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) { + handleResponseBody(returnValue, webRequest); } if (returnValue instanceof ModelAndView) { @@ -740,7 +740,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen else if (returnValue instanceof View) { 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); return new ModelAndView().addAllObjects(implicitModel); } @@ -771,7 +771,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } @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()); List acceptedMediaTypes = inputMessage.getHeaders().getAccept(); HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse()); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java index a465a1bac2..aa98634533 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolver.java @@ -39,6 +39,7 @@ import javax.servlet.http.HttpSession; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.ui.Model; import org.springframework.util.ClassUtils; 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 - * exceptions through the {@link ExceptionHandler} annotation. - *

This exception resolver is enabled by default in the {@link org.springframework.web.servlet.DispatcherServlet}. + * exceptions through the {@link ExceptionHandler} annotation.

This exception resolver is enabled by default in the + * {@link org.springframework.web.servlet.DispatcherServlet}. * * @author Arjen Poutsma * @since 3.0 @@ -110,7 +111,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc /** * 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 * @return the best matching method; or null if none is found */ @@ -132,23 +133,25 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc } else { Method oldMappedMethod = resolverMethods.get(handledException); - throw new IllegalStateException( - "Ambiguous exception handler mapped for " + handledException + "]: {" + - oldMappedMethod + ", " + method + "}."); + if (!oldMappedMethod.equals(method)) { + throw new IllegalStateException( + "Ambiguous exception handler mapped for " + handledException + "]: {" + + oldMappedMethod + ", " + method + "}."); + } } } } } }); + return getBestMatchingMethod(thrownException, resolverMethods); } /** - * Returns all the exception classes handled by the given method. - *

Default implementation looks for exceptions in the {@linkplain ExceptionHandler#value() annotation}, or - - * if that annotation element is empty - any exceptions listed in the method parameters if the method is annotated - * with {@code @ExceptionHandler}. + * Returns all the exception classes handled by the given method.

Default implementation looks for exceptions in the + * {@linkplain ExceptionHandler#value() annotation}, or - if that annotation element is empty - any exceptions listed + * in the method parameters if the method is annotated with {@code @ExceptionHandler}. * * @param method the method * @return the handled exceptions @@ -156,7 +159,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc @SuppressWarnings("unchecked") protected List> getHandledExceptions(Method method) { List> result = new ArrayList>(); - ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class); + ExceptionHandler exceptionHandler = AnnotationUtils.findAnnotation(method, ExceptionHandler.class); if (exceptionHandler != null) { if (!ObjectUtils.isEmpty(exceptionHandler.value())) { result.addAll(Arrays.asList(exceptionHandler.value())); @@ -172,9 +175,7 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc 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, Map, Method> resolverMethods) { 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, Object handler, NativeWebRequest webRequest, @@ -222,11 +221,11 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc } /** - * Resolves common method arguments. Delegates to registered {@link #setCustomArgumentResolver(WebArgumentResolver) argumentResolvers} first, - * then checking {@link #resolveStandardArgument}. + * Resolves common method arguments. Delegates to registered {@link #setCustomArgumentResolver(WebArgumentResolver) + * argumentResolvers} first, then checking {@link #resolveStandardArgument}. * * @param methodParameter the method parameter - * @param webRequest the request + * @param webRequest the request * @param thrownException the exception thrown * @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}, - * {@link ServletRequest}, {@link ServletResponse}, {@link HttpSession}, {@link Principal}, {@link Locale}, - * request {@link InputStream}, request {@link Reader}, response {@link OutputStream}, response {@link Writer}, - * and the given {@code thrownException}. + * Resolves standard method arguments. Default implementation handles {@link NativeWebRequest}, {@link ServletRequest}, + * {@link ServletResponse}, {@link HttpSession}, {@link Principal}, {@link Locale}, request {@link InputStream}, + * request {@link Reader}, response {@link OutputStream}, response {@link Writer}, and the given {@code + * thrownException}. * - * @param parameterType the method parameter type - * @param webRequest the request + * @param parameterType the method parameter type + * @param webRequest the request * @param thrownException the exception thrown * @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) throws Exception { - if (handlerMethod.isAnnotationPresent(ResponseStatus.class)) { - ResponseStatus responseStatus = handlerMethod.getAnnotation(ResponseStatus.class); + ResponseStatus responseStatus = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class); + if (responseStatus != null) { HttpServletResponse response = webRequest.getResponse(); 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> { private final Class handlerExceptionType; diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolverTests.java index 64771c27a6..a4d65f2ea7 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerExceptionResolverTests.java @@ -64,7 +64,17 @@ public class AnnotationMethodHandlerExceptionResolverTests { assertEquals("Invalid view name returned", "BindException", mav.getViewName()); 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) public void ambiguous() { IllegalArgumentException ex = new IllegalArgumentException(); @@ -86,6 +96,7 @@ public class AnnotationMethodHandlerExceptionResolverTests { private static class SimpleController { @ExceptionHandler(IOException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String handleIOException(IOException ex, HttpServletRequest request) { 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 private static class AmbiguousController { -- GitLab