SPR-8352 Init and apply MappedInterceptors from AbstractHandlerMapping

This commit is contained in:
Rossen Stoyanchev 2011-05-19 16:45:25 +00:00
parent 60aa598c03
commit 0bf92782ea
11 changed files with 298 additions and 341 deletions

View File

@ -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)
--------------------------------------

View File

@ -118,8 +118,8 @@ public class InterceptorConfigurer {
/**
* Returns all registered interceptors.
*/
protected MappedInterceptor[] getInterceptors() {
return mappedInterceptors.toArray(new MappedInterceptor[mappedInterceptors.size()]);
protected List<MappedInterceptor> getInterceptors() {
return mappedInterceptors;
}
}

View File

@ -124,8 +124,15 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar
private ApplicationContext applicationContext;
private List<MappedInterceptor> mappedInterceptors;
private List<HttpMessageConverter<?>> messageConverters;
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> 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<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
configurers.configureMessageConverters(converters);
if (converters.size() == 0) {
addDefaultHttpMessageConverters(converters);
}
adapter.setMessageConverters(converters);
return adapter;
}
@Bean(name="mvcConversionService")
private List<HttpMessageConverter<?>> getMessageConverters() {
if (messageConverters == null) {
messageConverters = new ArrayList<HttpMessageConverter<?>>();
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<HttpMessageConverter<?>> converters) {
private void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
converters.add(new ByteArrayHttpMessageConverter());
converters.add(stringConverter);
converters.add(new ResourceHttpMessageConverter());
converters.add(new SourceHttpMessageConverter<Source>());
converters.add(new XmlAwareFormHttpMessageConverter());
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
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<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
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) {
}
};
}

View File

@ -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.
*
* <p>Note: This base class does <i>not</i> 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<Object> interceptors = new ArrayList<Object>();
private HandlerInterceptor[] adaptedInterceptors;
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
/**
* 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).
* <p>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
* <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
* <p>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.
* <p>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.
* <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
* <p>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 <code>null</code> 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<MappedInterceptor> 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.
* <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
@ -169,10 +264,19 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
* @return the array of HandlerInterceptors, or <code>null</code> 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 <code>null</code> 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.
* <p>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.
* <p><b>NOTE:</b> 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;
}
}

View File

@ -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<String, Object> handlerMap = new LinkedHashMap<String, Object>();
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).
* <p>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
* <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
* <p>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.
* <p>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 {

View File

@ -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;
}
}
}

View File

@ -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<String, MappedInterceptor> 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<HandlerInterceptor> interceptors = new LinkedHashSet<HandlerInterceptor>();
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;
}
}
}

View File

@ -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<RequestMappingInfo> {
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<R
RequestMapping typeAnnot = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnot != null) {
RequestMappingInfo typeMapping = createFromRequestMapping(typeAnnot);
return typeMapping.combine(methodMapping, pathMatcher);
return typeMapping.combine(methodMapping, getPathMatcher());
}
else {
return methodMapping;
@ -161,7 +126,7 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo mapping,
String lookupPath,
HttpServletRequest request) {
return mapping.getMatchingRequestMapping(lookupPath, request, pathMatcher);
return mapping.getMatchingRequestMapping(lookupPath, request, getPathMatcher());
}
/**
@ -172,12 +137,18 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
return new RequestMappingInfoComparator(lookupPath, request);
}
/**
* Exposes URI template variables and producible media types as request attributes.
*
* @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
* @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
*/
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
String pattern = info.getPatterns().iterator().next();
Map<String, String> uriTemplateVariables = pathMatcher.extractUriTemplateVariables(pattern, lookupPath);
Map<String, String> 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<R
Set<MediaType> producibleMediaTypes = new HashSet<MediaType>();
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<R
}
}
/**
* Adds mapped interceptors to the handler execution chain.
*/
@Override
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
if (this.mappedInterceptors != null) {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
HandlerInterceptor[] handlerInterceptors = mappedInterceptors.getInterceptors(lookupPath, pathMatcher);
if (handlerInterceptors.length > 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<R
private List<MediaType> 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);

View File

@ -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<HandlerInterceptor> 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<HandlerInterceptor> 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<HandlerInterceptor> 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<HandlerInterceptor> 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<HandlerInterceptor> 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<HandlerInterceptor> 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<HandlerInterceptor> getInterceptorsForPath(String lookupPath) {
PathMatcher pathMatcher = new AntPathMatcher();
List<HandlerInterceptor> result = new ArrayList<HandlerInterceptor>();
for (MappedInterceptor interceptor : configurer.getInterceptors()) {
if (interceptor.matches(lookupPath, pathMatcher)) {
result.add(interceptor.getInterceptor());
}
}
return result;
}
private void verifyAdaptedInterceptor(HandlerInterceptor interceptor, TestWebRequestInterceptor webInterceptor)

View File

@ -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<List<HttpMessageConverter<?>>> converters = new Capture<List<HttpMessageConverter<?>>>();
Capture<FormattingConversionService> conversionService = new Capture<FormattingConversionService>();
Capture<List<HandlerMethodArgumentResolver>> resolvers = new Capture<List<HandlerMethodArgumentResolver>>();
Capture<List<HandlerMethodReturnValueHandler>> handlers = new Capture<List<HandlerMethodReturnValueHandler>>();
Capture<List<HttpMessageConverter<?>>> converters = new Capture<List<HttpMessageConverter<?>>>();
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

View File

@ -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")