跳至主要內容

10.HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

Java突击队大约 15 分钟

10.HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

Spring 版本:5.1.14.RELEASE

该系列其他文档请查看:《死磕 Spring MVC 源码分析 - 文章导读》open in new window

HandlerMapping 组件

HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors

  • handler 处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的 @RequestMapping 注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法
  • HandlerInterceptor 拦截器对处理请求进行增强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理

由于HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,所以将这部分内容拆分成了四个模块,依次进行分析:

HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

先来回顾一下HandlerMapping 接口体系的结构:

 

《HandlerMapping 组件(一)之 AbstractHandlerMapping》open in new window文档中已经分析了 HandlerMapping 组件的 AbstractHandlerMapping 抽象类基类

《HandlerMapping 组件(三)之 AbstractHandlerMethodMapping》open in new window文档中也已经分析了图中红色框部分的 AbstractHandlerMethodMapping 系,基于 Method 进行匹配。例如,我们所熟知的 @RequestMapping 等注解的方式。

那么本文就接着来分析图中黄色框部分的 AbstractUrlHandlerMapping 系,基于 URL 进行匹配。例如 《基于 XML 配置的 Spring MVC 简单的 HelloWorld 实例应用》open in new window ,当然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,还是使用这种方式。

因为 AbstractUrlHandlerMapping 在实际开发基本不会涉及到,所以本文选读,可以直接查看总结部分

一共有五个子类,分成两条线:

  • AbstractUrlHandlerMapping <= SimpleUrlHandlerMapping <= WebSocketHandlerMapping
  • AbstractUrlHandlerMapping <= AbstractDetectingUrlHandlerMapping <= BeanNameUrlHandlerMapping

其中,WebSocketHandlerMapping 是 spring-websocket 项目中的类,本文会无视它

所以,本文按照 AbstractUrlHandlerMapping、SimpleUrlHandlerMapping、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping 顺序进行分析

回顾

先来回顾一下在 DispatcherServlet 中处理请求的过程中通过 HandlerMapping 组件,获取到 HandlerExecutionChain 处理器执行链的方法,是通过AbstractHandlerMapping 的 getHandler 方法来获取的,如下:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // <1> 获得处理器(HandlerMethod 或者 HandlerExecutionChain),该方法是抽象方法,由子类实现
    Object handler = getHandlerInternal(request);
    // <2> 获得不到,则使用默认处理器
    // <3> 还是获得不到,则返回 null
    // <4> 如果找到的处理器是 String 类型,则从 Spring 容器中找到对应的 Bean 作为处理器
    // <5> 创建 HandlerExecutionChain 对象(包含处理器和拦截器)
    // ... 省略相关代码
    return executionChain;
}

在AbstractHandlerMapping 获取 HandlerExecutionChain 处理器执行链的方法中,需要先调用 getHandlerInternal(HttpServletRequest request) 抽象方法,获取请求对应的处理器,该方法由子类去实现,也就上图中黄色框红色框两类子类,本文分析黄色框部分内容

AbstractUrlHandlerMapping

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping,实现 MatchableHandlerMapping 接口,继承 AbstractHandlerMapping 抽象类,以 URL 作为 Handler 处理器 的 HandlerMapping 抽象类,提供 Handler 的获取、注册等等通用的骨架方法。

构造方法

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
	/**
	 * 根路径("/")的处理器
	 */
	@Nullable
	private Object rootHandler;

	/**
	 * 使用后置的 / 匹配
	 */
	private boolean useTrailingSlashMatch = false;

	/**
	 * 是否延迟加载处理器,默认关闭
	 */
	private boolean lazyInitHandlers = false;

	/**
	 * 路径和处理器的映射
	 *
	 * KEY:路径 {@link #lookupHandler(String, HttpServletRequest)}
	 */
	private final Map<String, Object> handlerMap = new LinkedHashMap<>();
}

registerHandler

registerHandler(String[] urlPaths, String beanName) 方法,注册多个 URL 的处理器,方法如下:

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);
    }
}

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;

    // Eagerly resolve handler if referencing singleton via name.
    // <1> 如果非延迟加载,并且 handler 为 String 类型,并且还是单例,则去获取 String 对应的 Bean 对象
    if (!this.lazyInitHandlers && handler instanceof String) {
        String handlerName = (String) handler;
        ApplicationContext applicationContext = obtainApplicationContext();
        if (applicationContext.isSingleton(handlerName)) {
            resolvedHandler = applicationContext.getBean(handlerName);
        }
    }

    // <2> 获得 urlPath 对应的处理器
    Object mappedHandler = this.handlerMap.get(urlPath);
    // <3> 检验 mappedHandler 是否已存在,如果已存在,并且不是当前 resolvedHandler 对象,则抛出异常
    if (mappedHandler != null) {
        if (mappedHandler != resolvedHandler) {
            throw new IllegalStateException(
                    "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                    "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
        }
    }
    else {
        // <4.1> 如果是 / 根路径,则设置为 rootHandler
        if (urlPath.equals("/")) {
            if (logger.isTraceEnabled()) {
                logger.trace("Root mapping to " + getHandlerDescription(handler));
            }
            setRootHandler(resolvedHandler);
        }
        // <4.2> 如果是 /* 路径,则设置为默认处理器
        else if (urlPath.equals("/*")) {
            if (logger.isTraceEnabled()) {
                logger.trace("Default mapping to " + getHandlerDescription(handler));
            }
            setDefaultHandler(resolvedHandler);
        }
        // <4.3> 添加到 handlerMap 中
        else {
            this.handlerMap.put(urlPath, resolvedHandler);
            if (logger.isTraceEnabled()) {
                logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
            }
        }
    }
}

遍历URL,依次注册处理器

1、 如果非延迟加载,并且handler为String类型,并且还是单例,则去获取String对应的Bean对象,resolvedHandler
2、handlerMap中获得urlPath对应的处理器;
3、 如果该路径已存在对应的处理器,但是不是当前resolvedHandler对象,则抛出异常;
4、 否则,该路径不存在对应的处理器,则将当前resolvedHandler处理器保存;

1、 如果是/根路径,则设置resolvedHandlerrootHandler
2、 否则,如果是/*路径,则设置为默认处理器defaultHandler(在父类中);
3、 否则,添加到handlerMap中;

getHandlerInternal

实现父类的 getHandlerInternal(HttpServletRequest request) 方法,获得处理器,方法如下:

@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    // <1> 获得请求的路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // <2> 获得处理器
    Object handler = lookupHandler(lookupPath, request);
    // <3> 如果找不到处理器,则使用 rootHandler 或 defaultHandler 处理器
    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;
        // <3.1> 如果是根路径,则使用 rootHandler 处理器
        if ("/".equals(lookupPath)) {
            rawHandler = getRootHandler();
        }
        // <3.2> 使用默认处理器
        if (rawHandler == null) {
            rawHandler = getDefaultHandler();
        }
        if (rawHandler != null) {
            // Bean name or resolved handler?
            // <3.3> 如果找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 作为处理器
            if (rawHandler instanceof String) {
                String handlerName = (String) rawHandler;
                rawHandler = obtainApplicationContext().getBean(handlerName);
            }
            // <3.4> 空方法,校验处理器。目前暂无子类实现该方法
            validateHandler(rawHandler, request);
            // <3.5> 创建处理器(HandlerExecutionChain 对象)
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
        }
    }
    return handler;
}

1、 获得请求路径;
2、 调用lookupHandler(StringurlPath,HttpServletRequestrequest)方法,获得处理器,详情见下文;
3、 如果找不到处理器,则使用rootHandlerdefaultHandler处理器;

1、 如果是/根路径,则使用rootHandler处理器;
2、 否则,使用defaultHandler默认处理器;
3、 如果找到的处理器是String类型,则从容器中找到该beanName对应的Bean作为处理器;
4、 调用validateHandler(Objecthandler,HttpServletRequestrequest),对处理器进行校验,空方法,暂无子类实现该方法;
5、 调用buildPathExposingHandler方法,创建HandlerExecutionChain处理器执行链,赋值给handler处理器,详情见下文;
4、 返回请求对应的handler处理器;

所以说这里但会的处理器对象可能是一个 HandlerExecutionChain 对象,用途目前不清楚😈 😈 先继续往下看

lookupHandler

lookupHandler(String urlPath, HttpServletRequest request) 方法,获得请求对应的处理器,方法如下:

@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
    // Direct match?
    // <1.1> 情况一,从 handlerMap 中,直接匹配处理器
    Object handler = this.handlerMap.get(urlPath);
    if (handler != null) {
        // Bean name or resolved handler?
        // <1.2> 如果找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 作为处理器
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }
        // <1.3> 空方法,校验处理器。目前暂无子类实现该方法
        validateHandler(handler, request);
        // <1.4> 创建处理器
        return buildPathExposingHandler(handler, urlPath, urlPath, null);
    }

    // Pattern match?
    List<String> matchingPatterns = new ArrayList<>();
    // <2.1> 情况二,Pattern 匹配合适的,并添加到 matchingPatterns 中
    for (String registeredPattern : this.handlerMap.keySet()) {
        if (getPathMatcher().match(registeredPattern, urlPath)) { // 路径通过Pattern匹配成功
            matchingPatterns.add(registeredPattern);
        }
        else if (useTrailingSlashMatch()) {
            if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                matchingPatterns.add(registeredPattern + "/");
            }
        }
    }

    // <2.2> 获得首个匹配(最优)的结果
    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) {
        // <2.3> 获得 bestMatch 对应的处理器
        handler = this.handlerMap.get(bestMatch);
        if (handler == null) {
            if (bestMatch.endsWith("/")) {
                handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
            }
            if (handler == null) { // 如果获得不到,抛出 IllegalStateException 异常
                throw new IllegalStateException(
                        "Could not find handler for best pattern match [" + bestMatch + "]");
            }
        }
        // <2.4> 如果找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 作为处理器
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }
        // <2.5> 空方法,校验处理器。目前暂无子类实现该方法
        validateHandler(handler, request);
        // <2.6> 获得匹配的路径
        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
        // <2.7> 获得路径参数集合
        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);
        }
        // <2.8> 创建处理器
        return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
    }

    // No handler found...
    return null;
}

1、 情况一

1、handlerMap中,直接匹配处理器;
2、 如果找到的处理器是String类型,则从容器中找到该beanName对应的Bean作为处理器;
3、 校验处理器,空方法,暂无子类实现,暂时忽略;
4、 创建处理器,直接返回,这里是HandlerExecutionChain类型,调用buildPathExposingHandler方法,详情见下文;
2、 情况二

1、 Pattern匹配合适的,并添加到matchingPatterns中;
2、 获得首个匹配(最优)的结果bestMatch
3、 获得bestMatch对应的处理器,如果获得不到,抛出异常;
4、 如果找到的处理器是String类型,则从容器中找到该beanName对应的Bean作为处理器;
5、 校验处理器,空方法,暂无子类实现,暂时忽略;
6、 获得请求最匹配的路径pathWithinMapping
7、 获得匹配的路径参数集合uriTemplateVariables
8、 创建处理器,直接返回,这里是HandlerExecutionChain类型,调用buildPathExposingHandler方法,详情见下文;
3、 都不匹配则返回null

buildPathExposingHandler

buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) 方法

构建一个 HandlerExecutionChain 类型的处理器,添加两个拦截器,方法如下:

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
        String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {

    // <1> 创建 HandlerExecutionChain 对象
    HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
    // <2> 添加 PathExposingHandlerInterceptor 拦截器,到 chain 中
    chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
    if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
        // <3> 添加 UriTemplateVariablesHandlerInterceptor 拦截器,到 chain 中
        chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
    }
    return chain;
}

1、 创建HandlerExecutionChain类型的处理器对象;
2、 添加PathExposingHandlerInterceptor拦截器,用于暴露bestMatchingPattern属性到请求中;
3、 添加UriTemplateVariablesHandlerInterceptor拦截器,用于暴露uriTemplateVariables属性到请求中;

两个拦截器如下:

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;
    }

}
protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping,
        HttpServletRequest request) {
    request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
    request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
}

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;
    }
}
protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
    request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
}

都是往请求中设置相关属性,用途目前不清楚😈 😈 先继续往下看

match

match(HttpServletRequest request, String pattern) 方法,执行匹配,代码如下:

@Override
@Nullable
public RequestMatchResult match(HttpServletRequest request, String pattern) {
    // <1> 获得请求路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // <2> 模式匹配,若匹配,则返回 RequestMatchResult 对象
    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;
}

1、 获得请求路径;
2、 模式匹配,若匹配,则返回RequestMatchResult对象;

SimpleUrlHandlerMapping

org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,继承 AbstractUrlHandlerMapping 抽象类,简单的就 URL 匹配的 HandlerMapping 实现类

使用示例

在接触Spring MVC 比较早,你也许见过这样配置

<!-- 定义一个 helloController Bean,实现了 Controller 接口 -->
<bean id="helloController" class="com.fullmoon.study.controller.HelloController"/>

<!-- 定义请求处理映射 HandlerMapping -->
<bean class="org.springframework.web.servlet.handler. SimpleUrlHandlerMapping">
    <property name="mappings" ref="urlMappings" />
</bean>

<!-- 定义请求映射表 map -->
<util:properties id="urlMappings">
    <prop key="/hello.form">helloController</prop>
</util:properties>

当然,上述这种配置基本已经不存在了,因为被 @RequestMapping 注解这样的方式所取代。更多的是 Spring MVC 自己内部的组件可能在使用这种类型的 HandlerMapping ,例如下图:

 

构造方法

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
    /**
     * 配置的 URL 与处理器的映射
     *
     * 最终,会调用 {@link #registerHandlers(Map)} 进行注册到 {@link AbstractUrlHandlerMapping#handlerMap} 中
     */
	private final Map<String, Object> urlMap = new LinkedHashMap<>();

	public void setMappings(Properties mappings) {
		CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
	}

	public void setUrlMap(Map<String, ?> urlMap) {
		this.urlMap.putAll(urlMap);
	}
}

例如上面的配置示例就会通过 setMappings(Properties mappings) 方法,将 /hello.formHelloController 设置到 urlMap

所以说处理器也可能是一个 Controller 接口

initApplicationContext

initApplicationContext()方法,用于初始化,将 urlMap 中的URL与处理器添加到父类的 handlerMap

在父类 WebApplicationObjectSupport 的父类 ApplicationObjectSupport 中可以看到,因为实现了 ApplicationContextAware 接口,则在初始化该 Bean 的时候会调用 setApplicationContext(@Nullable ApplicationContext context) 方法,在这个方法中会调用 initApplicationContext() 这个方法

在父类 AbstractHandlerMapping 中,该方法会初始化拦截器们

@Override
public void initApplicationContext() throws BeansException {
    super.initApplicationContext();
    registerHandlers(this.urlMap);
}

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
    if (urlMap.isEmpty()) {
        logger.trace("No patterns in " + formatMappingName());
    }
    else {
        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) {
                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());
        }
    }
}

逻辑很简单,调用父类的registerHandler(String urlPath, Object handler)方法,添加注册器

AbstractDetectingUrlHandlerMapping

org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping,继承 AbstractUrlHandlerMapping 抽象类,自动探测的基于 URL 匹配的 HandlerMapping 抽象实现类

构造方法

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

	/**
	 * 是否只扫描可访问的 Handler 们
	 */
	private boolean detectHandlersInAncestorContexts = false;

	public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) {
		this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts;
	}
}

initApplicationContext

initApplicationContext()方法,用于初始化,找到符合条件的处理器,添加到父类的 handlerMap

在父类 WebApplicationObjectSupport 的父类 ApplicationObjectSupport 中可以看到,因为实现了 ApplicationContextAware 接口,则在初始化该 Bean 的时候会调用 setApplicationContext(@Nullable ApplicationContext context) 方法,在这个方法中会调用 initApplicationContext() 这个方法

在父类 AbstractHandlerMapping 中,该方法会初始化拦截器们

@Override
public void initApplicationContext() throws ApplicationContextException {
    super.initApplicationContext();
    // 自动探测处理器
    detectHandlers();
}

protected void detectHandlers() throws BeansException {
    // <1> 从 Spring 上下文获取所有 Object 类型的 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> 如果该 Bean 存在对应的 URL,则添加该处理器
        if (!ObjectUtils.isEmpty(urls)) {
            // 调用父类的方法,往 handlerMap 中添加注册器
            registerHandler(urls, beanName);
        }
    }

    if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
        logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
    }
}

1、 从Spring上下文获取所有Object类型的Bean的名称们;
2、 遍历所有的Bean,逐个注册;

1、 获得Bean对应的URL们,调用determineUrlsForHandler(StringbeanName)抽象方法,交由子类实现,详情见BeanNameUrlHandlerMapping
2、 如果该Bean存在对应的URL,则添加该处理器,调用父类的方法,往handlerMap中添加注册器;

BeanNameUrlHandlerMapping

org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,继承 AbstractDetectingUrlHandlerMapping 抽象类,基于 Bean 的名字来自动探测的 HandlerMapping 实现类

使用示例

<!-- 定义一个 helloController Bean,实现了 Controller 接口 -->
<bean id="/hello.form" class="com.fullmoon.study.controller.HelloController"/>

和SimpleUrlHandlerMapping 不同,只需要设置它的 beanName 以 / 开头就好了,会被 BeanNameUrlHandlerMapping 探测到

determineUrlsForHandler

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		// 如果是以 / 开头,添加到 urls
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		// 获得 beanName 的别名们,如果以 / 开头,则添加到 urls
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}
}

逻辑很简单,如果 Bean 的名称或者别名是以 / 开头,则会作为一个 url 返回,父类则会将该 Bean 作为一个处理器

总结

在Spring MVC 处理请求的过程中,需要通过 HandlerMapping 组件会为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),该组件体系结构如下:

 

本文就黄色框中的内容进行了分析,基于 URL 进行匹配。如果你接触 Spring MVC 较早,可能见过 SimpleUrlHandlerMapping 和 BeanNameUrlHandlerMapping 中的使用示例的配置方式。当然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,还是使用这种方式。

相对来说逻辑比较简单,如果你有一个 Controller 或者 HttpRequestHandler 接口的实现类,有以下两种方式将其设置为处理器,可以处理请求

  • 配置 SimpleUrlHandlerMapping 类型的 HandlerMapping 对象,往它的 Map<String, Object> urlMap 中添加 url 与 Controller 实现类的映射就好了
  • 配置 BeanNameUrlHandlerMapping 类型的 HandlerMapping 对象,设置 Controller 实现类 的 beanName 为以 / 开头的名称就好了,它会探测到,将这个 Bean 的 beanName 作为 url,将 Controller 实现类 作为处理器

至此,HandlerMapping 组件就分析到这里了,相信你对 HandlerMapping 组件有了一个深入的了解,更加的清楚 Spring MVC 是如何处理器请求的

HandlerMapping 组件返回的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors),那么这个处理器是被谁调用的呢?

因为不同的 HandlerMapping 实现类返回的处理器类型可能不一样,如何执行这个处理器,这部分工作都交由 HandlerAdapter 组件(处理器的适配器)来完成

这里我们就把处理器理解为 HandlerMethod 处理器对象吧,因为我们平时使用最多的方式就是通过 @RequestMapping 注解来标注某个方法处理对应的请求

别慌,接下来分析 HandlerAdapter 组件不会特别复杂😈

参考文章:芋道源码《死磕 Spring MVC 源码分析》open in new window

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址:open in new window