# HandlerMapping

返回:spring mvc 九大组件

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

可以看到,除了一堆声明的常量外,其实就一个需要实现的方法 getHandler,该方法的返回值就是我们所了解到的 HandlerExecutionChain

p

这个继承关系虽然看着有点绕,其实仔细观察就两大类:

  • 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();
    // 三个方法都和拦截器有关
}
1
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) {
}
1
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());
}
1
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));
        }
    }
}
1
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;
}
1
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;
}
1
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;
}
1
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 添加一些参数。

# lookupHandler