提交 04fa5d4b 编写于 作者: A Arjen Poutsma

SPR-6093 - MVC Annotation Inheritance

上级 d8245c80
...@@ -341,7 +341,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen ...@@ -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.
...@@ -707,8 +707,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen ...@@ -707,8 +707,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
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 ...@@ -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 ...@@ -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 ...@@ -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; ...@@ -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; ...@@ -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
...@@ -132,23 +133,25 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc ...@@ -132,23 +133,25 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc
} }
else { else {
Method oldMappedMethod = resolverMethods.get(handledException); Method oldMappedMethod = resolverMethods.get(handledException);
if (!oldMappedMethod.equals(method)) {
throw new IllegalStateException( throw new IllegalStateException(
"Ambiguous exception handler mapped for " + handledException + "]: {" + "Ambiguous exception handler mapped for " + handledException + "]: {" +
oldMappedMethod + ", " + method + "}."); 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 ...@@ -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 ...@@ -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 ...@@ -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,8 +221,8 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc ...@@ -222,8 +221,8 @@ 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
...@@ -256,10 +255,10 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc ...@@ -256,10 +255,10 @@ 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
...@@ -327,8 +326,8 @@ public class AnnotationMethodHandlerExceptionResolver extends AbstractHandlerExc ...@@ -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 ...@@ -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;
......
...@@ -65,6 +65,16 @@ public class AnnotationMethodHandlerExceptionResolverTests { ...@@ -65,6 +65,16 @@ public class AnnotationMethodHandlerExceptionResolverTests {
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 { ...@@ -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 { ...@@ -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 {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册