1 /* 2 * Copyright 2002-2014 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.web.servlet.handler; 18 19 import java.util.ArrayList; 20 import java.util.Arrays; 21 import java.util.List; 22 import javax.servlet.http.HttpServletRequest; 23 24 import org.springframework.beans.BeansException; 25 import org.springframework.beans.factory.BeanFactoryUtils; 26 import org.springframework.core.Ordered; 27 import org.springframework.util.AntPathMatcher; 28 import org.springframework.util.Assert; 29 import org.springframework.util.PathMatcher; 30 import org.springframework.web.context.request.WebRequestInterceptor; 31 import org.springframework.web.context.support.WebApplicationObjectSupport; 32 import org.springframework.web.servlet.HandlerExecutionChain; 33 import org.springframework.web.servlet.HandlerInterceptor; 34 import org.springframework.web.servlet.HandlerMapping; 35 import org.springframework.web.util.UrlPathHelper; 36 37 /** 38 * Abstract base class for {@link org.springframework.web.servlet.HandlerMapping} 39 * implementations. Supports ordering, a default handler, handler interceptors, 40 * including handler interceptors mapped by path patterns. 41 * 42 * <p>Note: This base class does <i>not</i> support exposure of the 43 * {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}. Support for this attribute 44 * is up to concrete subclasses, typically based on request URL mappings. 45 * 46 * @author Juergen Hoeller 47 * @author Rossen Stoyanchev 48 * @since 07.04.2003 49 * @see #getHandlerInternal 50 * @see #setDefaultHandler 51 * @see #setAlwaysUseFullPath 52 * @see #setUrlDecode 53 * @see org.springframework.util.AntPathMatcher 54 * @see #setInterceptors 55 * @see org.springframework.web.servlet.HandlerInterceptor 56 */ 57 public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport 58 implements HandlerMapping, Ordered { 59 60 private int order = Integer.MAX_VALUE; // default: same as non-Ordered 61 62 private Object defaultHandler; 63 64 private UrlPathHelper urlPathHelper = new UrlPathHelper(); 65 66 private PathMatcher pathMatcher = new AntPathMatcher(); 67 68 private final List<Object> interceptors = new ArrayList<Object>(); 69 70 private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>(); 71 72 private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>(); 73 74 75 /** 76 * Specify the order value for this HandlerMapping bean. 77 * <p>Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered. 78 * @see org.springframework.core.Ordered#getOrder() 79 */ 80 public final void setOrder(int order) { 81 this.order = order; 82 } 83 84 @Override 85 public final int getOrder() { 86 return this.order; 87 } 88 89 /** 90 * Set the default handler for this handler mapping. 91 * This handler will be returned if no specific mapping was found. 92 * <p>Default is {@code null}, indicating no default handler. 93 */ 94 public void setDefaultHandler(Object defaultHandler) { 95 this.defaultHandler = defaultHandler; 96 } 97 98 /** 99 * Return the default handler for this handler mapping, 100 * or {@code null} if none. 101 */ 102 public Object getDefaultHandler() { 103 return this.defaultHandler; 104 } 105 106 /** 107 * Set if URL lookup should always use the full path within the current servlet 108 * context. Else, the path within the current servlet mapping is used if applicable 109 * (that is, in the case of a ".../*" servlet mapping in web.xml). 110 * <p>Default is "false". 111 * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath 112 */ 113 public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { 114 this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); 115 } 116 117 /** 118 * Set if context path and request URI should be URL-decoded. Both are returned 119 * <i>undecoded</i> by the Servlet API, in contrast to the servlet path. 120 * <p>Uses either the request encoding or the default encoding according 121 * to the Servlet spec (ISO-8859-1). 122 * @see org.springframework.web.util.UrlPathHelper#setUrlDecode 123 */ 124 public void setUrlDecode(boolean urlDecode) { 125 this.urlPathHelper.setUrlDecode(urlDecode); 126 } 127 128 /** 129 * Set if ";" (semicolon) content should be stripped from the request URI. 130 * <p>The default value is {@code true}. 131 * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean) 132 */ 133 public void setRemoveSemicolonContent(boolean removeSemicolonContent) { 134 this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent); 135 } 136 137 /** 138 * Set the UrlPathHelper to use for resolution of lookup paths. 139 * <p>Use this to override the default UrlPathHelper with a custom subclass, 140 * or to share common UrlPathHelper settings across multiple HandlerMappings 141 * and MethodNameResolvers. 142 */ 143 public void setUrlPathHelper(UrlPathHelper urlPathHelper) { 144 Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); 145 this.urlPathHelper = urlPathHelper; 146 } 147 148 /** 149 * Return the UrlPathHelper implementation to use for resolution of lookup paths. 150 */ 151 public UrlPathHelper getUrlPathHelper() { 152 return urlPathHelper; 153 } 154 155 /** 156 * Set the PathMatcher implementation to use for matching URL paths 157 * against registered URL patterns. Default is AntPathMatcher. 158 * @see org.springframework.util.AntPathMatcher 159 */ 160 public void setPathMatcher(PathMatcher pathMatcher) { 161 Assert.notNull(pathMatcher, "PathMatcher must not be null"); 162 this.pathMatcher = pathMatcher; 163 } 164 165 /** 166 * Return the PathMatcher implementation to use for matching URL paths 167 * against registered URL patterns. 168 */ 169 public PathMatcher getPathMatcher() { 170 return this.pathMatcher; 171 } 172 173 /** 174 * Set the interceptors to apply for all handlers mapped by this handler mapping. 175 * <p>Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor. 176 * Mapped interceptors apply only to request URLs that match its path patterns. 177 * Mapped interceptor beans are also detected by type during initialization. 178 * @param interceptors array of handler interceptors, or {@code null} if none 179 * @see #adaptInterceptor 180 * @see org.springframework.web.servlet.HandlerInterceptor 181 * @see org.springframework.web.context.request.WebRequestInterceptor 182 */ 183 public void setInterceptors(Object[] interceptors) { 184 this.interceptors.addAll(Arrays.asList(interceptors)); 185 } 186 187 188 /** 189 * Initializes the interceptors. 190 * @see #extendInterceptors(java.util.List) 191 * @see #initInterceptors() 192 */ 193 @Override 194 protected void initApplicationContext() throws BeansException { 195 extendInterceptors(this.interceptors); 196 detectMappedInterceptors(this.mappedInterceptors); 197 initInterceptors(); 198 } 199 200 /** 201 * Extension hook that subclasses can override to register additional interceptors, 202 * given the configured interceptors (see {@link #setInterceptors}). 203 * <p>Will be invoked before {@link #initInterceptors()} adapts the specified 204 * interceptors into {@link HandlerInterceptor} instances. 205 * <p>The default implementation is empty. 206 * @param interceptors the configured interceptor List (never {@code null}), allowing 207 * to add further interceptors before as well as after the existing interceptors 208 */ 209 protected void extendInterceptors(List<Object> interceptors) { 210 } 211 212 /** 213 * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors. 214 * <p>This is called in addition to any {@link MappedInterceptor}s that may have been provided 215 * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor} 216 * from the current context and its ancestors. Subclasses can override and refine this policy. 217 * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to 218 */ 219 protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) { 220 mappedInterceptors.addAll( 221 BeanFactoryUtils.beansOfTypeIncludingAncestors( 222 getApplicationContext(), MappedInterceptor.class, true, false).values()); 223 } 224 225 /** 226 * Initialize the specified interceptors, checking for {@link MappedInterceptor}s and 227 * adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary. 228 * @see #setInterceptors 229 * @see #adaptInterceptor 230 */ 231 protected void initInterceptors() { 232 if (!this.interceptors.isEmpty()) { 233 for (int i = 0; i < this.interceptors.size(); i++) { 234 Object interceptor = this.interceptors.get(i); 235 if (interceptor == null) { 236 throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); 237 } 238 if (interceptor instanceof MappedInterceptor) { 239 this.mappedInterceptors.add((MappedInterceptor) interceptor); 240 } 241 else { 242 this.adaptedInterceptors.add(adaptInterceptor(interceptor)); 243 } 244 } 245 } 246 } 247 248 /** 249 * Adapt the given interceptor object to the {@link HandlerInterceptor} interface. 250 * <p>By default, the supported interceptor types are {@link HandlerInterceptor} 251 * and {@link WebRequestInterceptor}. Each given {@link WebRequestInterceptor} 252 * will be wrapped in a {@link WebRequestHandlerInterceptorAdapter}. 253 * Can be overridden in subclasses. 254 * @param interceptor the specified interceptor object 255 * @return the interceptor wrapped as HandlerInterceptor 256 * @see org.springframework.web.servlet.HandlerInterceptor 257 * @see org.springframework.web.context.request.WebRequestInterceptor 258 * @see WebRequestHandlerInterceptorAdapter 259 */ 260 protected HandlerInterceptor adaptInterceptor(Object interceptor) { 261 if (interceptor instanceof HandlerInterceptor) { 262 return (HandlerInterceptor) interceptor; 263 } 264 else if (interceptor instanceof WebRequestInterceptor) { 265 return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor); 266 } 267 else { 268 throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName()); 269 } 270 } 271 272 /** 273 * Return the adapted interceptors as {@link HandlerInterceptor} array. 274 * @return the array of {@link HandlerInterceptor}s, or {@code null} if none 275 */ 276 protected final HandlerInterceptor[] getAdaptedInterceptors() { 277 int count = this.adaptedInterceptors.size(); 278 return (count > 0 ? this.adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null); 279 } 280 281 /** 282 * Return all configured {@link MappedInterceptor}s as an array. 283 * @return the array of {@link MappedInterceptor}s, or {@code null} if none 284 */ 285 protected final MappedInterceptor[] getMappedInterceptors() { 286 int count = this.mappedInterceptors.size(); 287 return (count > 0 ? this.mappedInterceptors.toArray(new MappedInterceptor[count]) : null); 288 } 289 290 /** 291 * Look up a handler for the given request, falling back to the default 292 * handler if no specific one is found. 293 * @param request current HTTP request 294 * @return the corresponding handler instance, or the default handler 295 * @see #getHandlerInternal 296 */ 297 @Override 298 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 299 Object handler = getHandlerInternal(request); 300 if (handler == null) { 301 handler = getDefaultHandler(); 302 } 303 if (handler == null) { 304 return null; 305 } 306 // Bean name or resolved handler? 307 if (handler instanceof String) { 308 String handlerName = (String) handler; 309 handler = getApplicationContext().getBean(handlerName); 310 } 311 return getHandlerExecutionChain(handler, request); 312 } 313 314 /** 315 * Look up a handler for the given request, returning {@code null} if no 316 * specific one is found. This method is called by {@link #getHandler}; 317 * a {@code null} return value will lead to the default handler, if one is set. 318 * <p>Note: This method may also return a pre-built {@link HandlerExecutionChain}, 319 * combining a handler object with dynamically determined interceptors. 320 * Statically specified interceptors will get merged into such an existing chain. 321 * @param request current HTTP request 322 * @return the corresponding handler instance, or {@code null} if none found 323 * @throws Exception if there is an internal error 324 */ 325 protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; 326 327 /** 328 * Build a {@link HandlerExecutionChain} for the given handler, including 329 * applicable interceptors. 330 * <p>The default implementation builds a standard {@link HandlerExecutionChain} 331 * with the given handler, the handler mapping's common interceptors, and any 332 * {@link MappedInterceptor}s matching to the current request URL. Subclasses 333 * may override this in order to extend/rearrange the list of interceptors. 334 * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a 335 * pre-built {@link HandlerExecutionChain}. This method should handle those 336 * two cases explicitly, either building a new {@link HandlerExecutionChain} 337 * or extending the existing chain. 338 * <p>For simply adding an interceptor in a custom subclass, consider calling 339 * {@code super.getHandlerExecutionChain(handler, request)} and invoking 340 * {@link HandlerExecutionChain#addInterceptor} on the returned chain object. 341 * @param handler the resolved handler instance (never {@code null}) 342 * @param request current HTTP request 343 * @return the HandlerExecutionChain (never {@code null}) 344 * @see #getAdaptedInterceptors() 345 */ 346 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { 347 HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? 348 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); 349 chain.addInterceptors(getAdaptedInterceptors()); 350 351 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); 352 for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) { 353 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { 354 chain.addInterceptor(mappedInterceptor.getInterceptor()); 355 } 356 } 357 358 return chain; 359 } 360 361 }