From f72769a62107f229e9b562304660e2eb4350bd18 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 7 Dec 2009 09:42:44 +0000 Subject: [PATCH] added order property to AnnotationMethodHandlerAdapter (SPR-6516) --- .../AnnotationMethodHandlerAdapter.java | 216 ++++++++++-------- .../AnnotationMethodHandlerAdapter.java | 171 ++++++++------ 2 files changed, 225 insertions(+), 162 deletions(-) diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java index e8fa23d6ac5..5ece3bbc877 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/mvc/annotation/AnnotationMethodHandlerAdapter.java @@ -59,6 +59,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.core.Ordered; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.style.StylerUtils; @@ -117,7 +118,8 @@ import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; * @see #setWebBindingInitializer * @see #setSessionAttributeStore */ -public class AnnotationMethodHandlerAdapter extends PortletContentGenerator implements HandlerAdapter, BeanFactoryAware { +public class AnnotationMethodHandlerAdapter extends PortletContentGenerator + implements HandlerAdapter, Ordered, BeanFactoryAware { private static final String IMPLICIT_MODEL_ATTRIBUTE = "org.springframework.web.portlet.mvc.ImplicitModel"; @@ -136,6 +138,8 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl private ModelAndViewResolver[] customModelAndViewResolvers; + private int order = Ordered.LOWEST_PRECEDENCE; + private ConfigurableBeanFactory beanFactory; private BeanExpressionContext expressionContext; @@ -242,6 +246,19 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl this.customModelAndViewResolvers = customModelAndViewResolvers; } + /** + * Specify the order value for this HandlerAdapter bean. + *

Default value is Integer.MAX_VALUE, meaning that it's non-ordered. + * @see org.springframework.core.Ordered#getOrder() + */ + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return this.order; + } + public void setBeanFactory(BeanFactory beanFactory) { if (beanFactory instanceof ConfigurableBeanFactory) { this.beanFactory = (ConfigurableBeanFactory) beanFactory; @@ -362,6 +379,9 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl } + /** + * Portlet-specific subclass of {@link HandlerMethodResolver}. + */ private static class PortletHandlerMethodResolver extends HandlerMethodResolver { private final Map mappings = new HashMap(); @@ -467,100 +487,9 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl } - private static class RequestMappingInfo { - - public final Set modes = new HashSet(); - - public String phase; - - public String value; - - public final Set methods = new HashSet(); - - public String[] params = new String[0]; - - public String[] headers = new String[0]; - - public void initStandardMapping(String[] modes, RequestMethod[] methods, String[] params, String[] headers) { - for (String mode : modes) { - this.modes.add(new PortletMode(mode)); - } - for (RequestMethod method : methods) { - this.methods.add(method.name()); - } - this.params = StringUtils.mergeStringArrays(this.params, params); - this.headers = StringUtils.mergeStringArrays(this.headers, headers); - } - - public void initPhaseMapping(String phase, String value, String[] params) { - if (this.phase != null) { - throw new IllegalStateException( - "Invalid mapping - more than one phase specified: '" + this.phase + "', '" + phase + "'"); - } - this.phase = phase; - this.value = value; - this.params = StringUtils.mergeStringArrays(this.params, params); - } - - public boolean match(PortletRequest request) { - if (!this.modes.isEmpty() && !this.modes.contains(request.getPortletMode())) { - return false; - } - if (StringUtils.hasLength(this.phase) && - !this.phase.equals(request.getAttribute(PortletRequest.LIFECYCLE_PHASE))) { - return false; - } - if (StringUtils.hasLength(this.value)) { - if (this.phase.equals(PortletRequest.ACTION_PHASE) && - !this.value.equals(request.getParameter(ActionRequest.ACTION_NAME))) { - return false; - } - else if (this.phase.equals(PortletRequest.RENDER_PHASE) && - !(new WindowState(this.value)).equals(request.getWindowState())) { - return false; - } - else if (this.phase.equals(PortletRequest.RESOURCE_PHASE) && - !this.value.equals(((ResourceRequest) request).getResourceID())) { - return false; - } - else if (this.phase.equals(PortletRequest.EVENT_PHASE)) { - Event event = ((EventRequest) request).getEvent(); - if (!this.value.equals(event.getName()) && !this.value.equals(event.getQName().toString())) { - return false; - } - } - } - return PortletAnnotationMappingUtils.checkRequestMethod(this.methods, request) && - PortletAnnotationMappingUtils.checkParameters(this.params, request) && - PortletAnnotationMappingUtils.checkHeaders(this.headers, request); - } - - public boolean isBetterMatchThan(RequestMappingInfo other) { - return ((!this.modes.isEmpty() && other.modes.isEmpty()) || - (StringUtils.hasLength(this.phase) && !StringUtils.hasLength(other.phase)) || - (StringUtils.hasLength(this.value) && !StringUtils.hasLength(other.value)) || - (!this.methods.isEmpty() && other.methods.isEmpty()) || - this.params.length > other.params.length); - } - - @Override - public boolean equals(Object obj) { - RequestMappingInfo other = (RequestMappingInfo) obj; - return (this.modes.equals(other.modes) && - ObjectUtils.nullSafeEquals(this.phase, other.phase) && - ObjectUtils.nullSafeEquals(this.value, other.value) && - this.methods.equals(other.methods) && - Arrays.equals(this.params, other.params) && - Arrays.equals(this.headers, other.headers)); - } - - @Override - public int hashCode() { - return (ObjectUtils.nullSafeHashCode(this.modes) * 29 + this.phase.hashCode()); - } - } - - + /** + * Portlet-specific subclass of {@link HandlerMethodInvoker}. + */ private class PortletHandlerMethodInvoker extends HandlerMethodInvoker { public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) { @@ -735,4 +664,101 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl } } + + /** + * Holder for request mapping metadata. Allows for finding a best matching candidate. + */ + private static class RequestMappingInfo { + + public final Set modes = new HashSet(); + + public String phase; + + public String value; + + public final Set methods = new HashSet(); + + public String[] params = new String[0]; + + public String[] headers = new String[0]; + + public void initStandardMapping(String[] modes, RequestMethod[] methods, String[] params, String[] headers) { + for (String mode : modes) { + this.modes.add(new PortletMode(mode)); + } + for (RequestMethod method : methods) { + this.methods.add(method.name()); + } + this.params = StringUtils.mergeStringArrays(this.params, params); + this.headers = StringUtils.mergeStringArrays(this.headers, headers); + } + + public void initPhaseMapping(String phase, String value, String[] params) { + if (this.phase != null) { + throw new IllegalStateException( + "Invalid mapping - more than one phase specified: '" + this.phase + "', '" + phase + "'"); + } + this.phase = phase; + this.value = value; + this.params = StringUtils.mergeStringArrays(this.params, params); + } + + public boolean match(PortletRequest request) { + if (!this.modes.isEmpty() && !this.modes.contains(request.getPortletMode())) { + return false; + } + if (StringUtils.hasLength(this.phase) && + !this.phase.equals(request.getAttribute(PortletRequest.LIFECYCLE_PHASE))) { + return false; + } + if (StringUtils.hasLength(this.value)) { + if (this.phase.equals(PortletRequest.ACTION_PHASE) && + !this.value.equals(request.getParameter(ActionRequest.ACTION_NAME))) { + return false; + } + else if (this.phase.equals(PortletRequest.RENDER_PHASE) && + !(new WindowState(this.value)).equals(request.getWindowState())) { + return false; + } + else if (this.phase.equals(PortletRequest.RESOURCE_PHASE) && + !this.value.equals(((ResourceRequest) request).getResourceID())) { + return false; + } + else if (this.phase.equals(PortletRequest.EVENT_PHASE)) { + Event event = ((EventRequest) request).getEvent(); + if (!this.value.equals(event.getName()) && !this.value.equals(event.getQName().toString())) { + return false; + } + } + } + return PortletAnnotationMappingUtils.checkRequestMethod(this.methods, request) && + PortletAnnotationMappingUtils.checkParameters(this.params, request) && + PortletAnnotationMappingUtils.checkHeaders(this.headers, request); + } + + public boolean isBetterMatchThan(RequestMappingInfo other) { + return ((!this.modes.isEmpty() && other.modes.isEmpty()) || + (StringUtils.hasLength(this.phase) && !StringUtils.hasLength(other.phase)) || + (StringUtils.hasLength(this.value) && !StringUtils.hasLength(other.value)) || + (!this.methods.isEmpty() && other.methods.isEmpty()) || + this.params.length > other.params.length); + } + + @Override + public boolean equals(Object obj) { + RequestMappingInfo other = (RequestMappingInfo) obj; + return (this.modes.equals(other.modes) && + ObjectUtils.nullSafeEquals(this.phase, other.phase) && + ObjectUtils.nullSafeEquals(this.value, other.value) && + this.methods.equals(other.methods) && + Arrays.equals(this.params, other.params) && + Arrays.equals(this.headers, other.headers)); + } + + @Override + public int hashCode() { + return (ObjectUtils.nullSafeHashCode(this.modes) * 29 + this.phase.hashCode()); + } + } + } 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 6f33665f96e..ef5e45fa9a8 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 @@ -52,6 +52,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; +import org.springframework.core.Ordered; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpInputMessage; @@ -127,22 +128,22 @@ import org.springframework.web.util.WebUtils; * @see #setSessionAttributeStore * @since 2.5 */ -public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, BeanFactoryAware { +public class AnnotationMethodHandlerAdapter extends WebContentGenerator + implements HandlerAdapter, Ordered, BeanFactoryAware { /** * Log category to use when no mapped handler is found for a request. - * * @see #pageNotFoundLogger */ public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; /** * Additional logger to use when no mapped handler is found for a request. - * * @see #PAGE_NOT_FOUND_LOG_CATEGORY */ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); + private UrlPathHelper urlPathHelper = new UrlPathHelper(); private PathMatcher pathMatcher = new AntPathMatcher(); @@ -167,6 +168,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(), new FormHttpMessageConverter(), new SourceHttpMessageConverter()}; + private int order = Ordered.LOWEST_PRECEDENCE; + private ConfigurableBeanFactory beanFactory; private BeanExpressionContext expressionContext; @@ -174,16 +177,18 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen private final Map, ServletHandlerMethodResolver> methodResolverCache = new ConcurrentHashMap, ServletHandlerMethodResolver>(); + public AnnotationMethodHandlerAdapter() { // no restriction of HTTP methods by default super(false); } + /** - * Set if URL lookup should always use the full path within the current servlet context. Else, the path within the - * current servlet mapping is used if applicable (that is, in the case of a ".../*" servlet mapping in web.xml). + * Set if URL lookup should always use the full path within the current servlet + * context. Else, the path within the current servlet mapping is used if applicable + * (that is, in the case of a ".../*" servlet mapping in web.xml). *

Default is "false". - * * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath */ public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { @@ -191,10 +196,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set if context path and request URI should be URL-decoded. Both are returned undecoded by the Servlet API, in - * contrast to the servlet path.

Uses either the request encoding or the default encoding according to the Servlet - * spec (ISO-8859-1). - * + * Set if context path and request URI should be URL-decoded. Both are returned + * undecoded by the Servlet API, in contrast to the servlet path. + *

Uses either the request encoding or the default encoding according + * to the Servlet spec (ISO-8859-1). * @see org.springframework.web.util.UrlPathHelper#setUrlDecode */ public void setUrlDecode(boolean urlDecode) { @@ -202,8 +207,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the UrlPathHelper to use for resolution of lookup paths.

Use this to override the default UrlPathHelper with - * a custom subclass, or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters. + * Set the UrlPathHelper to use for resolution of lookup paths. + *

Use this to override the default UrlPathHelper with a custom subclass, + * or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters. */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); @@ -211,10 +217,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is - * AntPathMatcher. - * - * @see org.springframework.util.AntPathMatcher + * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. + *

Default is {@link org.springframework.util.AntPathMatcher}. */ public void setPathMatcher(PathMatcher pathMatcher) { Assert.notNull(pathMatcher, "PathMatcher must not be null"); @@ -222,8 +226,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the MethodNameResolver to use for resolving default handler methods (carrying an empty - * @RequestMapping annotation).

Will only kick in when the handler method cannot be resolved uniquely + * Set the MethodNameResolver to use for resolving default handler methods + * (carrying an empty @RequestMapping annotation). + *

Will only kick in when the handler method cannot be resolved uniquely * through the annotation metadata already. */ public void setMethodNameResolver(MethodNameResolver methodNameResolver) { @@ -231,15 +236,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Specify a WebBindingInitializer which will apply pre-configured configuration to every DataBinder that this - * controller uses. + * Specify a WebBindingInitializer which will apply pre-configured + * configuration to every DataBinder that this controller uses. */ public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) { this.webBindingInitializer = webBindingInitializer; } /** - * Specify the strategy to store session attributes with.

Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, + * Specify the strategy to store session attributes with. + *

Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, * storing session attributes in the HttpSession, using the same attribute name as in the model. */ public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) { @@ -248,11 +254,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Cache content produced by @SessionAttributes annotated handlers for the given number of seconds. - * Default is 0, preventing caching completely.

In contrast to the "cacheSeconds" property which will apply to all - * general handlers (but not to @SessionAttributes annotated handlers), this setting will apply to - * @SessionAttributes annotated handlers only. - * + * Cache content produced by @SessionAttributes annotated handlers + * for the given number of seconds. Default is 0, preventing caching completely. + *

In contrast to the "cacheSeconds" property which will apply to all general handlers + * (but not to @SessionAttributes annotated handlers), this setting will + * apply to @SessionAttributes annotated handlers only. * @see #setCacheSeconds * @see org.springframework.web.bind.annotation.SessionAttributes */ @@ -261,15 +267,20 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set if controller execution should be synchronized on the session, to serialize parallel invocations from the same - * client.

More specifically, the execution of each handler method will get synchronized if this flag is "true". The - * best available session mutex will be used for the synchronization; ideally, this will be a mutex exposed by - * HttpSessionMutexListener.

The session mutex is guaranteed to be the same object during the entire lifetime of the - * session, available under the key defined by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a safe - * reference to synchronize on for locking on the current session.

In many cases, the HttpSession reference itself a - * safe mutex as well, since it will always be the same object reference for the same active logical session. However, - * this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex. - * + * Set if controller execution should be synchronized on the session, + * to serialize parallel invocations from the same client. + *

More specifically, the execution of the handleRequestInternal + * method will get synchronized if this flag is "true". The best available + * session mutex will be used for the synchronization; ideally, this will + * be a mutex exposed by HttpSessionMutexListener. + *

The session mutex is guaranteed to be the same object during + * the entire lifetime of the session, available under the key defined + * by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a + * safe reference to synchronize on for locking on the current session. + *

In many cases, the HttpSession reference itself is a safe mutex + * as well, since it will always be the same object reference for the + * same active logical session. However, this is not guaranteed across + * different servlet containers; the only 100% safe way is a session mutex. * @see org.springframework.web.util.HttpSessionMutexListener * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) */ @@ -278,61 +289,76 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } /** - * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed (e.g. for default attribute - * names).

Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}. + * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed + * (e.g. for default attribute names). + *

Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}. */ public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; } /** - * Set a custom WebArgumentResolvers to use for special method parameter types. Such a custom WebArgumentResolver will - * kick in first, having a chance to resolve an argument value before the standard argument handling kicks in. + * Set a custom WebArgumentResolvers to use for special method parameter types. + *

Such a custom WebArgumentResolver will kick in first, having a chance to resolve + * an argument value before the standard argument handling kicks in. */ public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) { - this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver}; + this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver}; } /** - * Set one or more custom WebArgumentResolvers to use for special method parameter types. Any such custom - * WebArgumentResolver will kick in first, having a chance to resolve an argument value before the standard argument - * handling kicks in. + * Set one or more custom WebArgumentResolvers to use for special method parameter types. + *

Any such custom WebArgumentResolver will kick in first, having a chance to resolve + * an argument value before the standard argument handling kicks in. */ public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) { this.customArgumentResolvers = argumentResolvers; } /** - * Set a custom ModelAndViewResolvers to use for special method return types. Such a custom ModelAndViewResolver will - * kick in first, having a chance to resolve an return value before the standard ModelAndView handling kicks in. + * Set a custom ModelAndViewResolvers to use for special method return types. + *

Such a custom ModelAndViewResolver will kick in first, having a chance to resolve + * a return value before the standard ModelAndView handling kicks in. */ public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) { - this.customModelAndViewResolvers = new ModelAndViewResolver[]{customModelAndViewResolver}; + this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver}; } /** - * Set one or more custom ModelAndViewResolvers to use for special method return types. Any such custom - * ModelAndViewResolver will kick in first, having a chance to resolve an return value before the standard ModelAndView - * handling kicks in. + * Set one or more custom ModelAndViewResolvers to use for special method return types. + *

Any such custom ModelAndViewResolver will kick in first, having a chance to resolve + * a return value before the standard ModelAndView handling kicks in. */ public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) { this.customModelAndViewResolvers = customModelAndViewResolvers; } /** - * Returns the message body converters to use. These converters are used to convert from and to HTTP requests and - * responses. + * Set the message body converters to use. + *

These converters are used to convert from and to HTTP requests and responses. + */ + public void setMessageConverters(HttpMessageConverter[] messageConverters) { + this.messageConverters = messageConverters; + } + + /** + * Return the message body converters that this adapter has been configured with. */ public HttpMessageConverter[] getMessageConverters() { return messageConverters; } /** - * Set the message body converters to use. These converters are used to convert from and to HTTP requests and - * responses. + * Specify the order value for this HandlerAdapter bean. + *

Default value is Integer.MAX_VALUE, meaning that it's non-ordered. + * @see org.springframework.core.Ordered#getOrder() */ - public void setMessageConverters(HttpMessageConverter[] messageConverters) { - this.messageConverters = messageConverters; + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return this.order; } public void setBeanFactory(BeanFactory beanFactory) { @@ -342,6 +368,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } + public boolean supports(Object handler) { return getMethodResolver(handler).hasHandlerMethods(); } @@ -393,7 +420,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen return -1; } - /** Build a HandlerMethodResolver for the given handler type. */ + /** + * Build a HandlerMethodResolver for the given handler type. + */ private ServletHandlerMethodResolver getMethodResolver(Object handler) { Class handlerClass = ClassUtils.getUserClass(handler); ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass); @@ -404,7 +433,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen return resolver; } - /** Servlet-specific subclass of {@link HandlerMethodResolver}. */ + + /** + * Servlet-specific subclass of {@link HandlerMethodResolver}. + */ private class ServletHandlerMethodResolver extends HandlerMethodResolver { private ServletHandlerMethodResolver(Class handlerType) { @@ -606,14 +638,17 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } - /** Servlet-specific subclass of {@link HandlerMethodInvoker}. */ + + /** + * Servlet-specific subclass of {@link HandlerMethodInvoker}. + */ private class ServletHandlerMethodInvoker extends HandlerMethodInvoker { private boolean responseArgumentUsed = false; private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) { super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer, - customArgumentResolvers, getMessageConverters()); + customArgumentResolvers, messageConverters); } @Override @@ -679,7 +714,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen @Override protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception { - HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse(); @@ -717,11 +751,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } @SuppressWarnings("unchecked") - public ModelAndView getModelAndView(Method handlerMethod, - Class handlerType, - Object returnValue, - ExtendedModelMap implicitModel, - ServletWebRequest webRequest) throws Exception { + public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue, + ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception { ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class); if (responseStatusAnn != null) { @@ -792,6 +823,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen @SuppressWarnings("unchecked") private void handleResponseBody(Object returnValue, ServletWebRequest webRequest) throws ServletException, IOException { + HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest()); List acceptedMediaTypes = inputMessage.getHeaders().getAccept(); if (acceptedMediaTypes.isEmpty()) { @@ -800,8 +832,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse()); Class returnValueType = returnValue.getClass(); List allSupportedMediaTypes = new ArrayList(); - if (getMessageConverters() != null) { - for (HttpMessageConverter messageConverter : getMessageConverters()) { + if (messageConverters != null) { + for (HttpMessageConverter messageConverter : messageConverters) { allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); for (MediaType acceptedMediaType : acceptedMediaTypes) { if (messageConverter.canWrite(returnValueType, acceptedMediaType)) { @@ -816,6 +848,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } + + /** + * Holder for request mapping metadata. Allows for finding a best matching candidate. + */ static class RequestMappingInfo { String[] paths = new String[0]; @@ -852,6 +888,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen } } + /** * Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will * result in: