/*
* Copyright 2002-2019 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
*
* https://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.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.HandlerExecutionChain;
/**
* URL 映射的抽象基类 {@link org.springframework.web.servlet.HandlerMapping}
* 实现。提供用于将处理程序映射到 URL 和可配置的基础结构
* 网址查询。有关后者的信息,请参阅“alwaysUseFullPath”属性。
*
* <p>支持直接匹配,例如注册的“/test”匹配“/test”,并且
* 各种 Ant 风格的模式匹配,例如注册的“/t*”模式匹配
* "/test" 和 "/team", "/test/*" 匹配 "/test" 目录中的所有路径,
* "/test/**" 匹配 "/test" 下的所有路径。有关详细信息,请参阅
* {@link org.springframework.util.AntPathMatcher AntPathMatcher} javadoc。
*
* <p>将搜索所有路径模式以找到最精确的匹配
* 当前请求路径。最精确的匹配被定义为最长的
* 匹配当前请求路径的路径模式。
*
* @author Juergen Hoeller
* @author Arjen Poutsma
* @since 16.04.2003
*/
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
@Nullable
private Object rootHandler;
private boolean useTrailingSlashMatch = false;
private boolean lazyInitHandlers = false;
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
/**
* 为这个处理程序映射设置根处理程序,即
* 要为根路径(“/”)注册的处理程序。
* <p>默认为 {@code null},表示没有根处理程序。
*/
public void setRootHandler(@Nullable Object rootHandler) {
this.rootHandler = rootHandler;
}
/**
* 返回此处理程序映射的根处理程序(为“/”注册),
* 或 {@code null} 如果没有。
*/
@Nullable
public Object getRootHandler() {
return this.rootHandler;
}
/**
* 是否与 URL 匹配而不考虑是否存在尾随斜杠。
* 如果启用,“/users”等 URL 模式也与“/users/”匹配。
* <p>默认值为{@code false}。
*/
public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) {
this.useTrailingSlashMatch = useTrailingSlashMatch;
}
/**
* 是否与 URL 匹配而不考虑是否存在尾随斜杠。
*/
public boolean useTrailingSlashMatch() {
return this.useTrailingSlashMatch;
}
/**
* 设置是否延迟初始化处理程序。仅适用于
* 单例处理程序,因为原型总是被延迟初始化。
* 默认为“false”,因为急切初始化可以提高效率
* 通过直接引用控制器对象。
* <p>如果你想让你的控制器延迟初始化,
* 使它们成为“lazy-init”并将此标志设置为 true。只是让他们
* "lazy-init" 不起作用,因为它们是通过
* 在这种情况下来自处理程序映射的引用。
*/
public void setLazyInitHandlers(boolean lazyInitHandlers) {
this.lazyInitHandlers = lazyInitHandlers;
}
/**
* 查找给定请求的URL路径的处理程序。
*
* @param request current HTTP request
* @return the handler instance, or {@code null} if none found
*/
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// 我们需要直接关心默认处理程序,因为我们需要
// 也为它公开 PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE。
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
/**
* 查找给定URL路径的处理程序实例。 * <p>
* 支持直接匹配,例如已注册的“ / test”匹配“ / test”,*,
* 以及各种Ant样式的模式匹配,例如注册的“ / t *”与“ / test”和“ / team”都匹配。
* 有关详细信息,请参见AntPathMatcher类。
* 找最精确的模式,其中最精确的定义为最长的路径模式。
*
* @param urlPath the URL the bean is mapped to
* @param request current HTTP request (to expose the path within the mapping to)
* @return the associated handler instance, or {@code null} if not found
* @see #exposePathWithinMapping
* @see org.springframework.util.AntPathMatcher
*/
@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
} else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
/**
* 针对当前请求验证给定的处理程序。
* <p>默认实现为空。可以在子类中覆盖,
* 例如,强制执行 URL 映射中表达的特定先决条件。
*
* @param handler the handler object to validate
* @param request current HTTP request
* @throws Exception if validation failed
*/
protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
}
/**
* 为给定的原始处理程序构建一个处理程序对象,在执行处理程序之前,将实际的处理程序,
* {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}以及{@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE}公开。
* 默认实现使用特殊的拦截器构建{@link HandlerExecutionChain}
* 该拦截器公开了path属性和uri模板变量
*
* @param rawHandler the raw handler to expose
* @param pathWithinMapping the path to expose before executing the handler
* @param uriTemplateVariables the URI template variables, can be {@code null} if no variables found
* @return the final handler object
*/
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
/**
* 将当前映射中的路径公开为请求属性。
*
* @param pathWithinMapping the path within the current mapping
* @param request the request to expose the path to
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
*/
protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping,
HttpServletRequest request) {
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
}
/**
* 将 URI 模板变量公开为请求属性。
*
* @param uriTemplateVariables the URI template variables
* @param request the request to expose the path to
* @see #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
*/
protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
}
@Override
@Nullable
public RequestMatchResult match(HttpServletRequest request, String pattern) {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request, LOOKUP_PATH);
if (getPathMatcher().match(pattern, lookupPath)) {
return new RequestMatchResult(pattern, lookupPath, getPathMatcher());
} else if (useTrailingSlashMatch()) {
if (!pattern.endsWith("/") && getPathMatcher().match(pattern + "/", lookupPath)) {
return new RequestMatchResult(pattern + "/", lookupPath, getPathMatcher());
}
}
return null;
}
/**
* 为给定的 URL 路径注册指定的处理程序。
*
* @param urlPaths the URLs that the bean should be mapped to
* @param beanName the name of the handler bean
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
/**
* 为给定的URL路径注册指定的处理程序。
*
* @param urlPath the URL the bean should be mapped to
* @param handler the handler instance or handler bean name String
* (a bean name will automatically be resolved into the corresponding handler bean)
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// 如果通过名称引用单例,则急切地解析处理程序。
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
} else {
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
} else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
} else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
private String getHandlerDescription(Object handler) {
return (handler instanceof String ? "'" + handler + "'" : handler.toString());
}
/**
* 将注册的处理程序作为不可修改的 Map 返回,并带有注册的路径
* 作为键和处理程序对象(或延迟初始化处理程序的处理程序 bean 名称)
* 作为价值。
*
* @see #getDefaultHandler()
*/
public final Map<String, Object> getHandlerMap() {
return Collections.unmodifiableMap(this.handlerMap);
}
/**
*指示此处理程序映射是否支持类型级映射。默认为 {@code false}。
*/
protected boolean supportsTypeLevelMappings() {
return false;
}
/**
* Special interceptor for exposing the
* {@link AbstractUrlHandlerMapping#PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE} attribute.
*
* @see AbstractUrlHandlerMapping#exposePathWithinMapping
*/
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
private final String bestMatchingPattern;
private final String pathWithinMapping;
public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
this.bestMatchingPattern = bestMatchingPattern;
this.pathWithinMapping = pathWithinMapping;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
}
}
/**
* 用于暴露的特殊拦截器
* {@link AbstractUrlHandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE} attribute.
*
* @see AbstractUrlHandlerMapping#exposePathWithinMapping
*/
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
private final Map<String, String> uriTemplateVariables;
public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
this.uriTemplateVariables = uriTemplateVariables;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
}
}
}
springmvc组件HandleMapping源码-AbstractUrlHandlerMapping
最新推荐文章于 2023-03-04 20:43:24 发布