艿艿:这是一篇相对选读的文章,因为 AbstractUrlHandlerMapping ,我们实际开发基本不会涉及。
1. 概述
本文接 《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(一)之 AbstractHandlerMapping》 一文,分享 AbstractHandlerMapping 的左半边 AbstractUrlHandlerMapping 系,即下图右半边红色部分:
- 一共有五个子类,分成两条线。
- AbstractUrlHandlerMapping <= SimpleUrlHandlerMapping <= WebSocketHandlerMapping 。
- AbstractUrlHandlerMapping <= AbstractDetectingUrlHandlerMapping <= BeanNameUrlHandlerMapping 。
- 其中,左下角的 WebSocketHandlerMapping 是
spring-websocket
项目中的类,本文会无视它。
所以,本文我们实际会是按照 AbstractUrlHandlerMapping、SimpleUrlHandlerMapping、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping 进行顺序分享。
2. AbstractUrlHandlerMapping
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
,实现 MatchableHandlerMapping 接口,继承 AbstractHandlerMapping 抽象类,以 URL 作为 Handler 的 HandlerMapping 抽象类,提供 Handler 的获取、注册等等通用的骨架方法。
2.1 构造方法
// AbstractUrlHandlerMapping.java |
2.2 registerHandler
#registerHandler(String[] urlPaths, String beanName)
方法,注册多个 URL 的处理器。代码如下:
// AbstractUrlHandlerMapping.java |
#registerHandler(String urlPath, Object handler)
方法,注册单个 URL 的处理器。代码如下:
// AbstractUrlHandlerMapping.java |
<1>
处,如果非延迟加载,并且handler
为 String 类型,并且还是单例,则去获取 String 对应的 Bean 对象。<2>
处,获得urlPath
对应的处理器。<3>
处,如果已经存在,并且和resolvedHandler
不同,则抛出 IllegalStateException 异常。<4.1>
处,如果是/
根路径,则设置处理器为rootHandler
。<4.2>
处,如果是/*
路径,则设置处理器为默认处理器。<4.3>
处,添加到handlerMap
中。
2.3 getHandlerInternal
实现 #getHandlerInternal(HttpServletRequest request)
方法,获得处理器。代码如下:
// AbstractUrlHandlerMapping.java |
<1>
处,获得请求的路径。<2>
处,调用#lookupHandler(String urlPath, HttpServletRequest request)
方法,获得处理器。详细解析,见 「2.4 lookupHandler」 。<3>
处,如果找不到处理器,则使用rootHandler
或defaultHandler
处理器。<3.1>
处, 如果是根路径,则使用rootHandler
处理器。<3.2>
处,使用默认处理器。<3.3>
处,如果找到的处理器是 String 类型,则从容器中找到 String 对应的 Bean 类型作为处理器。<3.4>
处,调用#validateHandler(Object handler, HttpServletRequest request)
方法,空方法,校验处理器。目前暂无子类实现该方法。代码如下:// AbstractUrlHandlerMapping.java
/**
* Validate the given handler against the current request.
* <p>The default implementation is empty. Can be overridden in subclasses,
* for example to enforce specific preconditions expressed in URL mappings.
* @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 {
}- 忽略忽略~
<3.5>
处,调用#buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables)
方法,构建暴露路径的 Handler 。详细解析,见 「2.5 buildPathExposingHandler」 。
2.4 lookupHandler
#lookupHandler(String urlPath, HttpServletRequest request)
方法,获得处理器。代码如下:
// AbstractUrlHandlerMapping.java |
- 逻辑有点长,整体分成两种情况,分别是直接匹配和 Pattern 模式匹配。
- ============ 情况一:直接匹配 ==========
<1.1>
处,从handlerMap
中,直接匹配处理器。<1.2>
处,如果找到的处理器是 String 类型,则从容器中找到 String 对应的 Bean 类型作为处理器。<1.3>
处,调用#validateHandler(Object handler, HttpServletRequest request)
方法,空方法,校验处理器。<1.4>
处,调用#buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables)
方法,构建暴露路径的 Handler 。详细解析,见 「2.5 buildPathExposingHandler」 。- ============ 情况二:模式匹配 ==========
<2.1>
处,遍历handlerMap
集合,逐个 Pattern 匹配合适的,并添加到matchingPatterns
中。<2.2>
处,获得首个匹配的结果patternComparator
。<2.3>
处,获得bestMatch
对应的处理器。<2.4>
处,如果找到的处理器是 String 类型,则从容器中找到 String 对应的 Bean 类型作为处理器。<2.5>
处,调用#validateHandler(Object handler, HttpServletRequest request)
方法,空方法,校验处理器。<2.6>
处,获得匹配的路径。😈 这块艿艿暂时没细看。<2.7>
处,获得路径参数集合uriTemplateVariables
。😈 这块艿艿也没细看。因为,可能存在多个最佳匹配,所以每个都会比较一次,全部添加到uriTemplateVariables
中。<2.8>
处,调用#buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables)
方法,构建暴露路径的 Handler 。详细解析,见 「2.5 buildPathExposingHandler」 。- ============ 情况三:都不匹配 ==========
<3>
处,都不匹配,返回null
。
2.5 buildPathExposingHandler
#buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, Map<String, String> uriTemplateVariables)
方法,构建暴露路径的 Handler 。代码如下:
// AbstractUrlHandlerMapping.java |
- 比较大的特点是,在
<1>
处,创建了 HandlerExecutionChain 对象,并且后续在<2.1>
和<2.2>
处,分别添加 PathExposingHandlerInterceptor 和 UriTemplateVariablesHandlerInterceptor 拦截器。用途在于通过这两个拦截器,暴露bestMatchingPattern
和uriTemplateVariables
到请求的属性中。 PathExposingHandlerInterceptor ,继承 HandlerInterceptorAdapter 类,是 AbstractUrlHandlerMapping 的内部类,代码如下:
// AbstractUrlHandlerMapping.java#PathExposingHandlerInterceptor.java
/**
* 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;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 暴露 BEST_MATCHING_PATTERN_ATTRIBUTE、PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 属性
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
// 暴露 INTROSPECT_TYPE_LEVEL_MAPPING 属性
request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
}
}
// AbstractUrlHandlerMapping.java
/**
* Expose the path within the current mapping as request attribute.
* @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);
}
UriTemplateVariablesHandlerInterceptor ,继承 HandlerInterceptorAdapter 类,是 AbstractUrlHandlerMapping 的内部类,代码如下:
// AbstractUrlHandlerMapping.java#PathExposingHandlerInterceptor.java
/**
* Special interceptor for exposing the
* {@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;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
}
}
// AbstractUrlHandlerMapping.java
/**
* Expose the URI templates variables as request attribute.
* @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);
}
2.6 match
#match(HttpServletRequest request, String pattern)
方法,执行匹配。代码如下:
// AbstractUrlHandlerMapping.java |
3. SimpleUrlHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
,继承 AbstractUrlHandlerMapping 抽象类,简单的 简单的 UrlHandlerMapping 实现类。
😈 如果胖友使用 Spring MVC 早,可能看过 《基于 XML 配置的Spring MVC 简单的 HelloWorld 实例应用》 这样的配置。当然,现在基本已经不存在了。因为,被 @RequestMapping
注解这样的方式所取代。更多的是 Spring MVC 自己内部的组件可能在使用,例如下图:使用情况
3.1 构造方法
// SimpleUrlHandlerMapping.java |
可以通过如下两个方法,设置到
urlMap
属性。代码如下:// SimpleUrlHandlerMapping.java
/**
* Map URL paths to handler bean names.
* This is the typical way of configuring this HandlerMapping.
* <p>Supports direct URL matches and Ant-style pattern matches. For syntax
* details, see the {@link org.springframework.util.AntPathMatcher} javadoc.
* @param mappings properties with URLs as keys and bean names as values
* @see #setUrlMap
*/
public void setMappings(Properties mappings) {
CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
}
/**
* Set a Map with URL paths as keys and handler beans (or handler bean names)
* as values. Convenient for population with bean references.
* <p>Supports direct URL matches and Ant-style pattern matches. For syntax
* details, see the {@link org.springframework.util.AntPathMatcher} javadoc.
* @param urlMap map with URLs as keys and beans as values
* @see #setMappings
*/
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
3.2 initApplicationContext
#initApplicationContext()
方法,进行初始化。代码如下:
// SimpleUrlHandlerMapping.java |
调用
#registerHandlers(Map<String, Object> urlMap)
方法,将urlMap
配置,注册到处理器。代码如下:// SimpleUrlHandlerMapping.java
/**
* Register all handlers specified in the URL map for the corresponding paths.
* @param urlMap a Map with URL paths as keys and handler beans or bean names as values
* @throws BeansException if a handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
// 为空,则仅打印日志
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
// 非空,则进行注册
} else {
// 遍历 urlMap 数组,逐个注册处理器
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) { // 附加 / 前缀
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) { // trim 方法,去掉头尾空格
handler = ((String) handler).trim();
}
// 【核心代码】注册处理器
registerHandler(url, handler);
});
// 打印日志
if (logger.isDebugEnabled()) {
List<String> patterns = new ArrayList<>();
if (getRootHandler() != null) {
patterns.add("/");
}
if (getDefaultHandler() != null) {
patterns.add("/**");
}
patterns.addAll(getHandlerMap().keySet());
logger.debug("Patterns " + patterns + " in " + formatMappingName());
}
}
}
4. AbstractDetectingUrlHandlerMapping
org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping
,继承 AbstractUrlHandlerMapping 抽象类,自动探测的 UrlHandlerMapping 抽象实现类。
4.1 构造方法
// AbstractDetectingUrlHandlerMapping.java |
4.2 initApplicationContext
#initApplicationContext()
方法,进行初始化。代码如下:
// AbstractDetectingUrlHandlerMapping.java |
调用
#detectHandlers()
方法,自动探测处理器。代码如下:// AbstractDetectingUrlHandlerMapping.java
/**
* Register all handlers found in the current ApplicationContext.
* <p>The actual URL determination for a handler is up to the concrete
* {@link #determineUrlsForHandler(String)} implementation. A bean for
* which no such URLs could be determined is simply not considered a handler.
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
* @see #determineUrlsForHandler(String)
*/
protected void detectHandlers() throws BeansException {
// <1> 获得 Bean 的名字的数组
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
// <2> 遍历 Bean ,逐个注册
for (String beanName : beanNames) {
// <2.1> 获得 Bean 对应的 URL 们
String[] urls = determineUrlsForHandler(beanName);
// <2.2> 如果 URL 们非空,则执行注册处理器
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}<1>
处,获得 Bean 的名字的数组。<2>
处,遍历 Bean ,逐个注册。<2.1>
处,调用#determineUrlsForHandler(String beanName)
抽象方法,获得 Bean 对应的 URL 们。代码如下:// AbstractDetectingUrlHandlerMapping.java
/**
* Determine the URLs for the given handler bean.
* @param beanName the name of the candidate bean
* @return the URLs determined for the bean, or an empty array if none
*/
protected abstract String[] determineUrlsForHandler(String beanName);- 这是 AbstractDetectingUrlHandlerMapping 的关键方法。但是,AbstractDetectingUrlHandlerMapping 只是搭建了自动探测的骨架。具体的探索逻辑,还是交给子类处理。
<2.2>
处,如果 URL 们非空,则调用父类 AbstractUrlHandlerMapping 的#registerHandler(String[] urlPaths, String beanName)
方法,执行注册处理器。
可能胖友看完这个类,一脸懵逼。不要方,继续来看 BeanNameUrlHandlerMapping 类。
5. BeanNameUrlHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
,继承 AbstractDetectingUrlHandlerMapping 抽象类,基于 Bean 的名字来自动探测的 HandlerMapping 实现类。
再看具体代码之前,我们先看看 《HandlerMapping 和 BeanNameUrlHandlerMapping 的使用》 。
然后,再来看一眼代码,如下:
// BeanNameUrlHandlerMapping.java |
- 是不是一眼就看明白了?哈哈哈哈
666. 彩蛋
卧槽,好简单的一篇,开心~~~
参考和推荐如下文章: