From 0bf92782ea4c8cba38ce18ece9cded2bf3b415e8 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 19 May 2011 16:45:25 +0000 Subject: [PATCH] SPR-8352 Init and apply MappedInterceptors from AbstractHandlerMapping --- .../resources/changelog.txt | 1 + .../annotation/InterceptorConfigurer.java | 4 +- .../annotation/WebMvcConfiguration.java | 102 ++++++------ .../handler/AbstractHandlerMapping.java | 145 ++++++++++++++++-- .../handler/AbstractUrlHandlerMapping.java | 97 +----------- .../servlet/handler/MappedInterceptor.java | 35 ++++- .../servlet/handler/MappedInterceptors.java | 73 --------- .../RequestMappingHandlerMapping.java | 69 ++------- .../InterceptorConfigurerTests.java | 70 +++++---- .../annotation/WebMvcConfigurationTests.java | 25 +-- .../RequestMappingHandlerMappingTests.java | 18 ++- 11 files changed, 298 insertions(+), 341 deletions(-) delete mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptors.java diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt index c3f8d904185..19db3fa4a08 100644 --- a/build-spring-framework/resources/changelog.txt +++ b/build-spring-framework/resources/changelog.txt @@ -18,6 +18,7 @@ Changes in version 3.1 M2 (2011-??-??) * support for URI template variables in view names with the "redirect:" prefix * a flag for extracting the value from single-key models in MappingJacksonJsonView * allow bean references in mvc:interceptor namespace elements +* consolidated the initialization and use of MappedInterceptors in AbstractHandlerMapping Changes in version 3.1 M1 (2011-02-11) -------------------------------------- diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java index 0cb67fb3bae..103770131dc 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java @@ -118,8 +118,8 @@ public class InterceptorConfigurer { /** * Returns all registered interceptors. */ - protected MappedInterceptor[] getInterceptors() { - return mappedInterceptors.toArray(new MappedInterceptor[mappedInterceptors.size()]); + protected List getInterceptors() { + return mappedInterceptors; } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java index 7986b999977..6f93cf62cf4 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java @@ -124,8 +124,15 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar private ApplicationContext applicationContext; + private List mappedInterceptors; + + private List> messageConverters; + @Autowired(required = false) public void setConfigurers(List configurers) { + if (configurers == null || configurers.isEmpty()) { + return; + } this.configurers.addWebMvcConfigurers(configurers); } @@ -136,21 +143,23 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } - + @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); - mapping.setMappedInterceptors(getMappedInterceptors()); + mapping.setInterceptors(getMappedInterceptors()); mapping.setOrder(0); return mapping; } - - private MappedInterceptor[] getMappedInterceptors() { - // TODO : prepare and store in instance var ? - InterceptorConfigurer configurer = new InterceptorConfigurer(); - configurers.configureInterceptors(configurer); - configurer.addInterceptor(new ConversionServiceExposingInterceptor(conversionService())); - return configurer.getInterceptors(); + + private Object[] getMappedInterceptors() { + if (mappedInterceptors == null) { + InterceptorConfigurer configurer = new InterceptorConfigurer(); + configurers.configureInterceptors(configurer); + configurer.addInterceptor(new ConversionServiceExposingInterceptor(conversionService())); + mappedInterceptors = configurer.getInterceptors(); + } + return mappedInterceptors.toArray(); } @Bean @@ -160,7 +169,7 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar configurers.configureViewControllers(configurer); SimpleUrlHandlerMapping handlerMapping = configurer.getHandlerMapping(); - handlerMapping.setMappedInterceptors(getMappedInterceptors()); + handlerMapping.setInterceptors(getMappedInterceptors()); return handlerMapping; } @@ -168,7 +177,7 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar public BeanNameUrlHandlerMapping beanNameHandlerMapping() { BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); mapping.setOrder(2); - mapping.setMappedInterceptors(getMappedInterceptors()); + mapping.setInterceptors(getMappedInterceptors()); return mapping; } @@ -190,6 +199,7 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); + adapter.setMessageConverters(getMessageConverters()); ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer(); bindingInitializer.setConversionService(conversionService()); @@ -204,24 +214,28 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar configurers.addReturnValueHandlers(returnValueHandlers); adapter.setCustomReturnValueHandlers(returnValueHandlers); - List> converters = new ArrayList>(); - configurers.configureMessageConverters(converters); - if (converters.size() == 0) { - addDefaultHttpMessageConverters(converters); - } - adapter.setMessageConverters(converters); - return adapter; } - @Bean(name="mvcConversionService") + private List> getMessageConverters() { + if (messageConverters == null) { + messageConverters = new ArrayList>(); + configurers.configureMessageConverters(messageConverters); + if (messageConverters.isEmpty()) { + addDefaultHttpMessageConverters(messageConverters); + } + } + return messageConverters; + } + + @Bean(name="webMvcConversionService") public FormattingConversionService conversionService() { FormattingConversionService conversionService = new DefaultFormattingConversionService(); configurers.addFormatters(conversionService); return conversionService; } - @Bean(name="mvcValidator") + @Bean(name="webMvcValidator") public Validator validator() { Validator validator = configurers.getValidator(); if (validator != null) { @@ -240,37 +254,30 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar return (Validator) BeanUtils.instantiate(clazz); } else { - return new Validator() { - public void validate(Object target, Errors errors) { - } - - public boolean supports(Class clazz) { - return false; - } - }; + return NOOP_VALIDATOR; } } - private void addDefaultHttpMessageConverters(List> converters) { + private void addDefaultHttpMessageConverters(List> messageConverters) { StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); stringConverter.setWriteAcceptCharset(false); - converters.add(new ByteArrayHttpMessageConverter()); - converters.add(stringConverter); - converters.add(new ResourceHttpMessageConverter()); - converters.add(new SourceHttpMessageConverter()); - converters.add(new XmlAwareFormHttpMessageConverter()); + messageConverters.add(new ByteArrayHttpMessageConverter()); + messageConverters.add(stringConverter); + messageConverters.add(new ResourceHttpMessageConverter()); + messageConverters.add(new SourceHttpMessageConverter()); + messageConverters.add(new XmlAwareFormHttpMessageConverter()); ClassLoader classLoader = getClass().getClassLoader(); if (ClassUtils.isPresent("javax.xml.bind.Binder", classLoader)) { - converters.add(new Jaxb2RootElementHttpMessageConverter()); + messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } if (ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoader)) { - converters.add(new MappingJacksonHttpMessageConverter()); + messageConverters.add(new MappingJacksonHttpMessageConverter()); } if (ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", classLoader)) { - converters.add(new AtomFeedHttpMessageConverter()); - converters.add(new RssChannelHttpMessageConverter()); + messageConverters.add(new AtomFeedHttpMessageConverter()); + messageConverters.add(new RssChannelHttpMessageConverter()); } } @@ -303,16 +310,19 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar private HandlerExceptionResolver createExceptionHandlerExceptionResolver() throws Exception { ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver(); - - List> converters = new ArrayList>(); - configurers.configureMessageConverters(converters); - if (converters.size() == 0) { - addDefaultHttpMessageConverters(converters); - } - resolver.setMessageConverters(converters); - + resolver.setMessageConverters(getMessageConverters()); resolver.afterPropertiesSet(); return resolver; } + + private static final Validator NOOP_VALIDATOR = new Validator() { + + public boolean supports(Class clazz) { + return false; + } + + public void validate(Object target, Errors errors) { + } + }; } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java index d6ec8ee1bd9..0d4b303bdde 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java @@ -19,28 +19,39 @@ package org.springframework.web.servlet.handler; import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import javax.servlet.http.HttpServletRequest; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.core.Ordered; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.Assert; +import org.springframework.util.PathMatcher; import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.context.support.WebApplicationObjectSupport; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; +import org.springframework.web.util.UrlPathHelper; /** * Abstract base class for {@link org.springframework.web.servlet.HandlerMapping} - * implementations. Supports ordering, a default handler, and handler interceptors. + * implementations. Supports ordering, a default handler, handler interceptors, + * including handler interceptors mapped by path patterns. * *

Note: This base class does not support exposure of the * {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}. Support for this attribute * is up to concrete subclasses, typically based on request URL mappings. * * @author Juergen Hoeller + * @author Rossen Stoyanchev * @since 07.04.2003 * @see #getHandlerInternal * @see #setDefaultHandler + * @see #setAlwaysUseFullPath + * @see #setUrlDecode + * @see org.springframework.util.AntPathMatcher * @see #setInterceptors * @see org.springframework.web.servlet.HandlerInterceptor */ @@ -51,10 +62,15 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport private Object defaultHandler; + private UrlPathHelper urlPathHelper = new UrlPathHelper(); + + private PathMatcher pathMatcher = new AntPathMatcher(); + private final List interceptors = new ArrayList(); - private HandlerInterceptor[] adaptedInterceptors; + private final List adaptedInterceptors = new ArrayList(); + private final List mappedInterceptors = new ArrayList(); /** * Specify the order value for this HandlerMapping bean. @@ -86,9 +102,69 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport return this.defaultHandler; } + /** + * 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) { + this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); + } + + /** + * 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) { + this.urlPathHelper.setUrlDecode(urlDecode); + } + + /** + * 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 MethodNameResolvers. + */ + public void setUrlPathHelper(UrlPathHelper urlPathHelper) { + Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); + this.urlPathHelper = urlPathHelper; + } + + /** + * Return the UrlPathHelper implementation to use for resolution of lookup paths. + */ + public UrlPathHelper getUrlPathHelper() { + return urlPathHelper; + } + + /** + * Set the PathMatcher implementation to use for matching URL paths + * against registered URL patterns. Default is AntPathMatcher. + * @see org.springframework.util.AntPathMatcher + */ + public void setPathMatcher(PathMatcher pathMatcher) { + Assert.notNull(pathMatcher, "PathMatcher must not be null"); + this.pathMatcher = pathMatcher; + } + + /** + * Return the PathMatcher implementation to use for matching URL paths + * against registered URL patterns. + */ + public PathMatcher getPathMatcher() { + return this.pathMatcher; + } + /** * Set the interceptors to apply for all handlers mapped by this handler mapping. - *

Supported interceptor types are HandlerInterceptor and WebRequestInterceptor. + *

Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor. + * Mapped interceptors apply only to request URLs that match its path patterns. + * Mapped interceptor beans are also detected by type during initialization. * @param interceptors array of handler interceptors, or null if none * @see #adaptInterceptor * @see org.springframework.web.servlet.HandlerInterceptor @@ -107,6 +183,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); + detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); } @@ -124,23 +201,41 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport } /** - * Initialize the specified interceptors, adapting them where necessary. + * Detects beans of type {@link MappedInterceptor} and adds them to the list of mapped interceptors. + * This is done in addition to any {@link MappedInterceptor}s that may have been provided via + * {@link #setInterceptors(Object[])}. Subclasses can override this method to change that. + * + * @param mappedInterceptors an empty list to add MappedInterceptor types to + */ + protected void detectMappedInterceptors(List mappedInterceptors) { + mappedInterceptors.addAll( + BeanFactoryUtils.beansOfTypeIncludingAncestors( + getApplicationContext(),MappedInterceptor.class, true, false).values()); + } + + /** + * Initialize the specified interceptors, checking for {@link MappedInterceptor}s and adapting + * HandlerInterceptors where necessary. * @see #setInterceptors * @see #adaptInterceptor */ protected void initInterceptors() { if (!this.interceptors.isEmpty()) { - this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()]; for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } - this.adaptedInterceptors[i] = adaptInterceptor(interceptor); + if (interceptor instanceof MappedInterceptor) { + mappedInterceptors.add((MappedInterceptor) interceptor); + } + else { + adaptedInterceptors.add(adaptInterceptor(interceptor)); + } } } } - + /** * Adapt the given interceptor object to the HandlerInterceptor interface. *

Supported interceptor types are HandlerInterceptor and WebRequestInterceptor. @@ -169,10 +264,19 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport * @return the array of HandlerInterceptors, or null if none */ protected final HandlerInterceptor[] getAdaptedInterceptors() { - return this.adaptedInterceptors; + int count = adaptedInterceptors.size(); + return (count > 0) ? adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null; } - + /** + * Return all configured {@link MappedInterceptor}s as an array. + * @return the array of {@link MappedInterceptor}s, or null if none + */ + protected final MappedInterceptor[] getMappedInterceptors() { + int count = mappedInterceptors.size(); + return (count > 0) ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null; + } + /** * Look up a handler for the given request, falling back to the default * handler if no specific one is found. @@ -212,7 +316,8 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport /** * Build a HandlerExecutionChain for the given handler, including applicable interceptors. *

The default implementation simply builds a standard HandlerExecutionChain with - * the given handler and this handler mapping's common interceptors. Subclasses may + * the given handler, the handler mapping's common interceptors, and any {@link MappedInterceptor}s + * matching to the current request URL. Subclasses may * override this in order to extend/rearrange the list of interceptors. *

NOTE: The passed-in handler object may be a raw handler or a pre-built * HandlerExecutionChain. This method should handle those two cases explicitly, @@ -225,14 +330,20 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport * @see #getAdaptedInterceptors() */ protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { - if (handler instanceof HandlerExecutionChain) { - HandlerExecutionChain chain = (HandlerExecutionChain) handler; - chain.addInterceptors(getAdaptedInterceptors()); - return chain; - } - else { - return new HandlerExecutionChain(handler, getAdaptedInterceptors()); + HandlerExecutionChain chain = + (handler instanceof HandlerExecutionChain) ? + (HandlerExecutionChain) handler : new HandlerExecutionChain(handler); + + chain.addInterceptors(getAdaptedInterceptors()); + + String lookupPath = urlPathHelper.getLookupPathForRequest(request); + for (MappedInterceptor mappedInterceptor : mappedInterceptors) { + if (mappedInterceptor.matches(lookupPath, pathMatcher)) { + chain.addInterceptor(mappedInterceptor.getInterceptor()); + } } + + return chain; } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java index ff5d9be5851..75ba08c540f 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java @@ -27,14 +27,10 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.BeansException; -import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; -import org.springframework.util.PathMatcher; import org.springframework.web.servlet.HandlerExecutionChain; -import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.util.UrlPathHelper; /** * Abstract base class for URL-mapped {@link org.springframework.web.servlet.HandlerMapping} @@ -54,76 +50,15 @@ import org.springframework.web.util.UrlPathHelper; * @author Juergen Hoeller * @author Arjen Poutsma * @since 16.04.2003 - * @see #setAlwaysUseFullPath - * @see #setUrlDecode - * @see org.springframework.util.AntPathMatcher */ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { - private UrlPathHelper urlPathHelper = new UrlPathHelper(); - - private PathMatcher pathMatcher = new AntPathMatcher(); - private Object rootHandler; private boolean lazyInitHandlers = false; private final Map handlerMap = new LinkedHashMap(); - private MappedInterceptors mappedInterceptors; - - - /** - * 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) { - this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); - } - - /** - * 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) { - this.urlPathHelper.setUrlDecode(urlDecode); - } - - /** - * 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 MethodNameResolvers. - * @see org.springframework.web.servlet.mvc.multiaction.AbstractUrlMethodNameResolver#setUrlPathHelper - */ - public void setUrlPathHelper(UrlPathHelper urlPathHelper) { - Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); - this.urlPathHelper = urlPathHelper; - } - - /** - * Set the PathMatcher implementation to use for matching URL paths - * against registered URL patterns. Default is AntPathMatcher. - * @see org.springframework.util.AntPathMatcher - */ - public void setPathMatcher(PathMatcher pathMatcher) { - Assert.notNull(pathMatcher, "PathMatcher must not be null"); - this.pathMatcher = pathMatcher; - } - - /** - * Return the PathMatcher implementation to use for matching URL paths - * against registered URL patterns. - */ - public PathMatcher getPathMatcher() { - return this.pathMatcher; - } /** * Set the root handler for this handler mapping, that is, @@ -156,19 +91,6 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { this.lazyInitHandlers = lazyInitHandlers; } - public void setMappedInterceptors(MappedInterceptor[] mappedInterceptors) { - this.mappedInterceptors = new MappedInterceptors(mappedInterceptors); - } - - - @Override - protected void initInterceptors() { - super.initInterceptors(); - if (mappedInterceptors == null) { - this.mappedInterceptors = MappedInterceptors.createFromDeclaredBeans(getApplicationContext()); - } - } - /** * Look up a handler for the URL path of the given request. * @param request current HTTP request @@ -176,7 +98,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { */ @Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { - String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); + String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to @@ -286,19 +208,6 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { */ protected void validateHandler(Object handler, HttpServletRequest request) throws Exception { } - - @Override - protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { - HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request); - if (this.mappedInterceptors != null) { - String lookupPath = urlPathHelper.getLookupPathForRequest(request); - HandlerInterceptor[] handlerInterceptors = mappedInterceptors.getInterceptors(lookupPath, pathMatcher); - if (handlerInterceptors.length > 0) { - chain.addInterceptors(handlerInterceptors); - } - } - return chain; - } /** * Build a handler object for the given raw handler, exposing the actual @@ -434,7 +343,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { /** * Special interceptor for exposing the * {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute. - * @link AbstractUrlHandlerMapping#exposePathWithinMapping + * @see AbstractUrlHandlerMapping#exposePathWithinMapping */ private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter { @@ -459,7 +368,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping { /** * Special interceptor for exposing the * {@link AbstractUrlHandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE} attribute. - * @link AbstractUrlHandlerMapping#exposePathWithinMapping + * @see AbstractUrlHandlerMapping#exposePathWithinMapping */ private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java index f1ba8a303b9..93e11d2ebbc 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptor.java @@ -16,13 +16,16 @@ package org.springframework.web.servlet.handler; +import org.springframework.util.PathMatcher; import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.servlet.HandlerInterceptor; /** * Holds information about a HandlerInterceptor mapped to a path into the application. + * Provides a method to match a request path to the mapped path patterns. * * @author Keith Donald + * @author Rossen Stoyanchev * @since 3.0 */ public final class MappedInterceptor { @@ -33,9 +36,9 @@ public final class MappedInterceptor { /** - * Create a new mapped interceptor. - * @param pathPatterns the path patterns - * @param interceptor the interceptor + * Create a new MappedInterceptor instance. + * @param pathPatterns the path patterns to map with a {@code null} value matching to all paths + * @param interceptor the HandlerInterceptor instance to map to the given patterns */ public MappedInterceptor(String[] pathPatterns, HandlerInterceptor interceptor) { this.pathPatterns = pathPatterns; @@ -43,9 +46,9 @@ public final class MappedInterceptor { } /** - * Create a new mapped interceptor. - * @param pathPatterns the path patterns - * @param interceptor the interceptor + * Create a new MappedInterceptor instance. + * @param pathPatterns the path patterns to map with a {@code null} value matching to all paths + * @param interceptor the WebRequestInterceptor instance to map to the given patterns */ public MappedInterceptor(String[] pathPatterns, WebRequestInterceptor interceptor) { this.pathPatterns = pathPatterns; @@ -66,5 +69,23 @@ public final class MappedInterceptor { public HandlerInterceptor getInterceptor() { return this.interceptor; } - + + /** + * Returns {@code true} if the interceptor applies to the given request path. + * @param lookupPath the current request path + * @param pathMatcher a path matcher for path pattern matching + */ + public boolean matches(String lookupPath, PathMatcher pathMatcher) { + if (pathPatterns == null) { + return true; + } + else { + for (String pathPattern : pathPatterns) { + if (pathMatcher.match(pathPattern, lookupPath)) { + return true; + } + } + return false; + } + } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptors.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptors.java deleted file mode 100644 index 766cd306250..00000000000 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/MappedInterceptors.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2011 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.servlet.handler; - -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.util.PathMatcher; -import org.springframework.web.servlet.HandlerInterceptor; - -public final class MappedInterceptors { - - private MappedInterceptor[] mappedInterceptors; - - public MappedInterceptors(MappedInterceptor[] mappedInterceptors) { - this.mappedInterceptors = mappedInterceptors; - } - - public static MappedInterceptors createFromDeclaredBeans(ListableBeanFactory beanFactory) { - Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, - MappedInterceptor.class, true, false); - - if (!beans.isEmpty()) { - return new MappedInterceptors(beans.values().toArray(new MappedInterceptor[beans.size()])); - } - else { - return null; - } - } - - public HandlerInterceptor[] getInterceptors(String lookupPath, PathMatcher pathMatcher) { - Set interceptors = new LinkedHashSet(); - for (MappedInterceptor interceptor : this.mappedInterceptors) { - if (matches(interceptor, lookupPath, pathMatcher)) { - interceptors.add(interceptor.getInterceptor()); - } - } - return interceptors.toArray(new HandlerInterceptor[interceptors.size()]); - } - - private boolean matches(MappedInterceptor interceptor, String lookupPath, PathMatcher pathMatcher) { - String[] pathPatterns = interceptor.getPathPatterns(); - if (pathPatterns != null) { - for (String pattern : pathPatterns) { - if (pathMatcher.match(pattern, lookupPath)) { - return true; - } - } - return false; - } - else { - return true; - } - } - -} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index bcdc982fa0f..1ac4ac66632 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -25,14 +25,13 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.Assert; import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.web.HttpMediaTypeNotAcceptableException; @@ -41,12 +40,8 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.HandlerExecutionChain; -import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; -import org.springframework.web.servlet.handler.MappedInterceptor; -import org.springframework.web.servlet.handler.MappedInterceptors; import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory; /** @@ -59,36 +54,6 @@ import org.springframework.web.servlet.mvc.method.condition.RequestConditionFact */ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping { - private PathMatcher pathMatcher = new AntPathMatcher(); - - private MappedInterceptors mappedInterceptors; - - /** - * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is - * AntPathMatcher. - * - * @see org.springframework.util.AntPathMatcher - */ - public void setPathMatcher(PathMatcher pathMatcher) { - Assert.notNull(pathMatcher, "PathMatcher must not be null"); - this.pathMatcher = pathMatcher; - } - - /** - * Set the {@link MappedInterceptor} instances to use to intercept handler method invocations. - */ - public void setMappedInterceptors(MappedInterceptor[] mappedInterceptors) { - this.mappedInterceptors = new MappedInterceptors(mappedInterceptors); - } - - @Override - protected void initInterceptors() { - super.initInterceptors(); - if (this.mappedInterceptors == null) { - this.mappedInterceptors = MappedInterceptors.createFromDeclaredBeans(getApplicationContext()); - } - } - /** * {@inheritDoc} The handler determination in this method is made based on the presence of a type-level {@link * Controller} annotation. @@ -127,7 +92,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping uriTemplateVariables = pathMatcher.extractUriTemplateVariables(pattern, lookupPath); + Map uriTemplateVariables = getPathMatcher().extractUriTemplateVariables(pattern, lookupPath); request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables); if (!info.getProduces().isEmpty()) { @@ -200,7 +171,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping producibleMediaTypes = new HashSet(); for (RequestMappingInfo info : requestMappingInfos) { for (String pattern : info.getPatterns()) { - if (pathMatcher.match(pattern, lookupPath)) { + if (getPathMatcher().match(pattern, lookupPath)) { if (!info.getMethods().match(request)) { for (RequestMethod method : info.getMethods().getMethods()) { allowedMethods.add(method.name()); @@ -233,22 +204,6 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping 0) { - chain.addInterceptors(handlerInterceptors); - } - } - return chain; - } - /** * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a specific * request. For example not all {@link RequestMappingInfo} patterns may apply to the current request. Therefore an @@ -267,7 +222,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping requestAcceptHeader; public RequestMappingInfoComparator(String lookupPath, HttpServletRequest request) { - this.patternComparator = pathMatcher.getPatternComparator(lookupPath); + this.patternComparator = getPathMatcher().getPatternComparator(lookupPath); String acceptHeader = request.getHeader("Accept"); this.requestAcceptHeader = MediaType.parseMediaTypes(acceptHeader); MediaType.sortByQualityValue(this.requestAcceptHeader); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java index 74434f38079..180a88fb1c4 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java @@ -16,20 +16,24 @@ package org.springframework.web.servlet.config.annotation; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.ui.ModelMap; import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.handler.MappedInterceptors; +import org.springframework.web.servlet.handler.MappedInterceptor; import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.theme.ThemeChangeInterceptor; @@ -66,15 +70,15 @@ public class InterceptorConfigurerTests { @Test public void addInterceptor() { configurer.addInterceptor(interceptor1); - HandlerInterceptor[] interceptors = getInterceptorsForPath(null); - assertArrayEquals(new HandlerInterceptor[] {interceptor1}, interceptors); + List interceptors = getInterceptorsForPath(null); + assertEquals(Arrays.asList(interceptor1), interceptors); } @Test public void addInterceptors() { configurer.addInterceptors(interceptor1, interceptor2); - HandlerInterceptor[] interceptors = getInterceptorsForPath(null); - assertArrayEquals(new HandlerInterceptor[] {interceptor1, interceptor2}, interceptors); + List interceptors = getInterceptorsForPath(null); + assertEquals(Arrays.asList(interceptor1, interceptor2), interceptors); } @Test @@ -82,35 +86,35 @@ public class InterceptorConfigurerTests { configurer.mapInterceptor(new String[] {"/path1"}, interceptor1); configurer.mapInterceptor(new String[] {"/path2"}, interceptor2); - assertArrayEquals(new HandlerInterceptor[] {interceptor1}, getInterceptorsForPath("/path1")); - assertArrayEquals(new HandlerInterceptor[] {interceptor2}, getInterceptorsForPath("/path2")); + assertEquals(Arrays.asList(interceptor1), getInterceptorsForPath("/path1")); + assertEquals(Arrays.asList(interceptor2), getInterceptorsForPath("/path2")); } @Test public void mapInterceptors() { configurer.mapInterceptors(new String[] {"/path1"}, interceptor1, interceptor2); - assertArrayEquals(new HandlerInterceptor[] {interceptor1, interceptor2}, getInterceptorsForPath("/path1")); - assertArrayEquals(new HandlerInterceptor[] {}, getInterceptorsForPath("/path2")); + assertEquals(Arrays.asList(interceptor1, interceptor2), getInterceptorsForPath("/path1")); + assertEquals(Arrays.asList(), getInterceptorsForPath("/path2")); } @Test public void addWebRequestInterceptor() throws Exception { configurer.addInterceptor(webRequestInterceptor1); - HandlerInterceptor[] interceptors = getInterceptorsForPath(null); + List interceptors = getInterceptorsForPath(null); - assertEquals(1, interceptors.length); - verifyAdaptedInterceptor(interceptors[0], webRequestInterceptor1); + assertEquals(1, interceptors.size()); + verifyAdaptedInterceptor(interceptors.get(0), webRequestInterceptor1); } @Test public void addWebRequestInterceptors() throws Exception { configurer.addInterceptors(webRequestInterceptor1, webRequestInterceptor2); - HandlerInterceptor[] interceptors = getInterceptorsForPath(null); + List interceptors = getInterceptorsForPath(null); - assertEquals(2, interceptors.length); - verifyAdaptedInterceptor(interceptors[0], webRequestInterceptor1); - verifyAdaptedInterceptor(interceptors[1], webRequestInterceptor2); + assertEquals(2, interceptors.size()); + verifyAdaptedInterceptor(interceptors.get(0), webRequestInterceptor1); + verifyAdaptedInterceptor(interceptors.get(1), webRequestInterceptor2); } @Test @@ -118,30 +122,36 @@ public class InterceptorConfigurerTests { configurer.mapInterceptor(new String[] {"/path1"}, webRequestInterceptor1); configurer.mapInterceptor(new String[] {"/path2"}, webRequestInterceptor2); - HandlerInterceptor[] interceptors = getInterceptorsForPath("/path1"); - assertEquals(1, interceptors.length); - verifyAdaptedInterceptor(interceptors[0], webRequestInterceptor1); + List interceptors = getInterceptorsForPath("/path1"); + assertEquals(1, interceptors.size()); + verifyAdaptedInterceptor(interceptors.get(0), webRequestInterceptor1); interceptors = getInterceptorsForPath("/path2"); - assertEquals(1, interceptors.length); - verifyAdaptedInterceptor(interceptors[0], webRequestInterceptor2); + assertEquals(1, interceptors.size()); + verifyAdaptedInterceptor(interceptors.get(0), webRequestInterceptor2); } @Test public void mapWebRequestInterceptor2() throws Exception { configurer.mapInterceptors(new String[] {"/path1"}, webRequestInterceptor1, webRequestInterceptor2); - HandlerInterceptor[] interceptors = getInterceptorsForPath("/path1"); - assertEquals(2, interceptors.length); - verifyAdaptedInterceptor(interceptors[0], webRequestInterceptor1); - verifyAdaptedInterceptor(interceptors[1], webRequestInterceptor2); + List interceptors = getInterceptorsForPath("/path1"); + assertEquals(2, interceptors.size()); + verifyAdaptedInterceptor(interceptors.get(0), webRequestInterceptor1); + verifyAdaptedInterceptor(interceptors.get(1), webRequestInterceptor2); - assertEquals(0, getInterceptorsForPath("/path2").length); + assertEquals(0, getInterceptorsForPath("/path2").size()); } - private HandlerInterceptor[] getInterceptorsForPath(String lookupPath) { - MappedInterceptors mappedInterceptors = new MappedInterceptors(configurer.getInterceptors()); - return mappedInterceptors.getInterceptors(lookupPath, new AntPathMatcher()); + private List getInterceptorsForPath(String lookupPath) { + PathMatcher pathMatcher = new AntPathMatcher(); + List result = new ArrayList(); + for (MappedInterceptor interceptor : configurer.getInterceptors()) { + if (interceptor.matches(lookupPath, pathMatcher)) { + result.add(interceptor.getInterceptor()); + } + } + return result; } private void verifyAdaptedInterceptor(HandlerInterceptor interceptor, TestWebRequestInterceptor webInterceptor) diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java index 2093d876687..8b51cc1b523 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java @@ -16,16 +16,25 @@ package org.springframework.web.servlet.config.annotation; +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; + import javax.servlet.http.HttpServletRequest; import org.easymock.Capture; import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; - import org.springframework.format.support.FormattingConversionService; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; @@ -46,9 +55,6 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; -import static org.easymock.EasyMock.*; -import static org.junit.Assert.*; - /** * A test fixture for WebMvcConfiguration tests. * @@ -67,17 +73,17 @@ public class WebMvcConfigurationTests { } @Test - public void annotationHandlerAdapter() { + public void annotationHandlerAdapter() throws Exception { + Capture>> converters = new Capture>>(); Capture conversionService = new Capture(); Capture> resolvers = new Capture>(); Capture> handlers = new Capture>(); - Capture>> converters = new Capture>>(); + configurer.configureMessageConverters(capture(converters)); expect(configurer.getValidator()).andReturn(null); configurer.addFormatters(capture(conversionService)); configurer.addArgumentResolvers(capture(resolvers)); configurer.addReturnValueHandlers(capture(handlers)); - configurer.configureMessageConverters(capture(converters)); replay(configurer); mvcConfiguration.setConfigurers(Arrays.asList(configurer)); @@ -107,7 +113,8 @@ public class WebMvcConfigurationTests { converters.add(new StringHttpMessageConverter()); } }); - mvcConfiguration.setConfigurers(configurers ); + mvcConfiguration = new WebMvcConfiguration(); + mvcConfiguration.setConfigurers(configurers); adapter = mvcConfiguration.requestMappingHandlerAdapter(); assertEquals("Only one custom converter should be registered", 1, adapter.getMessageConverters().size()); @@ -187,7 +194,7 @@ public class WebMvcConfigurationTests { hm.setApplicationContext(context); HandlerExecutionChain chain = hm.getHandler(request); assertNotNull("No chain returned", chain); - assertNotNull("Expected at one default converter", chain.getInterceptors()); + assertNotNull("Expected at least one default converter", chain.getInterceptors()); } @Controller diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index 6d50fced7a7..99da971c7e6 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -140,19 +140,25 @@ public class RequestMappingHandlerMappingTests { } @Test - public void mappedInterceptors() { - String path = "/handle"; + public void mappedInterceptors() throws Exception { + String path = "/foo"; HandlerInterceptor interceptor = new HandlerInterceptorAdapter() {}; MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] {path}, interceptor); - mapping.setMappedInterceptors(new MappedInterceptor[] { mappedInterceptor }); + StaticApplicationContext context = new StaticApplicationContext(); + context.registerSingleton("handler", handler.getClass()); - HandlerExecutionChain chain = mapping.getHandlerExecutionChain(handler, new MockHttpServletRequest("GET", path)); + mapping = new RequestMappingHandlerMapping(); + mapping.setInterceptors(new Object[] { mappedInterceptor }); + mapping.setApplicationContext(context); + + HandlerExecutionChain chain = mapping.getHandler(new MockHttpServletRequest("GET", path)); + assertNotNull(chain); assertNotNull(chain.getInterceptors()); assertSame(interceptor, chain.getInterceptors()[0]); - chain = mapping.getHandlerExecutionChain(handler, new MockHttpServletRequest("GET", "/invalid")); - assertNull(chain.getInterceptors()); + chain = mapping.getHandler(new MockHttpServletRequest("GET", "/invalid")); + assertNull(chain); } @SuppressWarnings("unused")