# HandlerMapping
HandlerMapping
HandlerMapping 叫做处理器映射器,它的作用就是根据当前 request 找到对应的 Handler 和 Interceptor,然后封装成一个 HandlerExecutionChain 对象返回
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
@Deprecated
String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
default boolean usesPathPatterns() {
return false;
}
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以看到,除了一堆声明的常量外,其实就一个需要实现的方法 getHandler,该方法的返回值就是我们所了解到的 HandlerExecutionChain。

这个继承关系虽然看着有点绕,其实仔细观察就两大类:
- AbstractHandlerMethodMapping
- AbstractHandlerMethodMapping 体系下的都是根据方法名进行匹配的
- AbstractUrlHandlerMapping
- AbstractUrlHandlerMapping 体系下的都是根据 URL 路径进行匹配的
这两者有一个共同的父类 AbstractHandlerMapping,其他的都是一些辅助接口。
# AbstractHandlerMapping
AbstractHandlerMapping 实现了 HandlerMapping 接口,无论是通过 URL 进行匹配还是通过方法名进行匹配,都是通过继承 AbstractHandlerMapping 来实现的,所以 AbstractHandlerMapping 所做的事情其实就是一些公共的事情,将以一些需要具体处理的事情则交给子类去处理,这其实就是典型的模版方法模式。
AbstractHandlerMapping 间接继承自 ApplicationObjectSupport,并重写了 initApplicationContext 方法(其实该方法也是一个模版方法),这也是 AbstractHandlerMapping 的初始化入口方法
/**
* Initializes the interceptors.
* @see #extendInterceptors(java.util.List)
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
// 三个方法都和拦截器有关
}
2
3
4
5
6
7
8
9
10
11
12
# extendInterceptors
/**
* Extension hook that subclasses can override to register additional interceptors,
* given the configured interceptors (see {@link #setInterceptors}).
* <p>Will be invoked before {@link #initInterceptors()} adapts the specified
* interceptors into {@link HandlerInterceptor} instances.
* <p>The default implementation is empty.
* @param interceptors the configured interceptor List (never {@code null}), allowing
* to add further interceptors before as well as after the existing interceptors
*/
protected void extendInterceptors(List<Object> interceptors) {
}
2
3
4
5
6
7
8
9
10
11
extendInterceptors 是一个模版方法,可以在子类中实现,子类实现了该方法之后,可以对拦截器进行添加、删除或者修改,不过在 SpringMVC 的具体实现中,其实这个方法并没有在子类中进行实现。
# detectMappedInterceptors
/**
* Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors.
* <p>This is called in addition to any {@link MappedInterceptor MappedInterceptors} that may have been provided
* via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor}
* from the current context and its ancestors. Subclasses can override and refine this policy.
* @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to
*/
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
2
3
4
5
6
7
8
9
10
11
12
detectMappedInterceptors 方法会从 SpringMVC 容器以及 Spring 容器中查找所有 MappedInterceptor 类型的 Bean,查找到之后添加到 mappedInterceptors 属性中(其实就是全局的 adaptedInterceptors 属性)。
一般来说,我们定义好一个拦截器之后,还要在 XML 文件中配置该拦截器,拦截器以及各种配置信息,最终就会被封装成一个 MappedInterceptor 对象。
# initInterceptors
/**
* Initialize the specified interceptors, checking for {@link MappedInterceptor MappedInterceptors} and
* adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor HandlerInterceptor}s and
* {@link WebRequestInterceptor}s if necessary.
* @see #setInterceptors
* @see #adaptInterceptor
*/
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
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.add(adaptInterceptor(interceptor));
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
initInterceptors 方法主要是进行拦截器的初始化操作,具体内容是将 interceptors 集合中的拦截器添加到 adaptedInterceptors 集合中。
至此,我们看到,所有拦截器最终都会被存入 adaptedInterceptors 变量中。
AbstractHandlerMapping 的初始化其实也就是拦截器的初始化过程。
# getHandler
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- 首先调用 getHandlerInternal 方法去尝试获取处理器,getHandlerInternal 方法也是一个模版方法,该方法将在子类中实现。
- 如果没找到相应的处理器,则调用 getDefaultHandler 方法获取默认的处理器,我们在配置 HandlerMapping 的时候可以配置默认的处理器。
- 如果找到的处理器是一个字符串,则根据该字符串找去 SpringMVC 容器中找到对应的 Bean。
- 确保 lookupPath 存在,一会找对应的拦截器的时候会用到。
- 找到 handler 之后,接下来再调用 getHandlerExecutionChain 方法获取 HandlerExecutionChain 对象。
- 接下来 if 里边的是进行跨域处理的,获取到跨域的相关配置,然后进行验证&配置,检查是否允许跨域。跨域这块的配置以及校验还是蛮有意思的,松哥以后专门写文章来和小伙伴们细聊。
# getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里直接根据已有的 handler 创建一个新的 HandlerExecutionChain 对象,然后遍历 adaptedInterceptors 集合,该集合里存放的都是拦截器,如果拦截器的类型是 MappedInterceptor,则调用 matches 方法去匹配一下,看一下是否是拦截当前请求的拦截器,如果是,则调用 chain.addInterceptor 方法加入到 HandlerExecutionChain 对象中;如果就是一个普通拦截器,则直接加入到 HandlerExecutionChain 对象中。
这就是 AbstractHandlerMapping#getHandler 方法的大致逻辑,可以看到,这里留了一个模版方法 getHandlerInternal 在子类中实现,接下来我们就来看看它的子类。
# AbstractUrlHandlerMapping
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
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
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 首先找到 lookupPath,就是请求的路径。这个方法本身松哥就不多说了,之前在Spring5 里边的新玩法!这种 URL 请求让我涨见识了!一文中有过介绍。
- 接下来就是调用 lookupHandler 方法获取 Handler 对象,lookupHandler 有一个重载方法,具体用哪个,主要看所使用的 URL 匹配模式,如果使用了最新的 PathPattern(Spring5 之后的),则使用三个参数的 lookupHandler;如果还是使用之前旧的 AntPathMatcher,则这里使用两个参数的 lookupHandler。
- 如果前面没有获取到 handler 实例,则接下来再做各种尝试,去分别查找 RootHandler、DefaultHandler 等,如果找到的 Handler 是一个 String,则去 Spring 容器中查找该 String 对应的 Bean,再调用 validateHandler 方法来校验找到的 handler 和 request 是否匹配,不过这是一个空方法,子类也没有实现,所以可以忽略之。最后再通过 buildPathExposingHandler 方法给找到的 handler 添加一些参数。