精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

该系列文档是本人在学习 Spring MVC 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读html

Spring 版本:5.2.4.RELEASEjava

该系列其余文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》git

HandlerMapping 组件

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

  • handler 处理器是 Object 类型,能够将其理解成 HandlerMethod 对象(例如咱们使用最多的 @RequestMapping 注解所标注的方法会解析成该对象),包含了方法的全部信息,经过该对象可以执行该方法web

  • HandlerInterceptor 拦截器对处理请求进行加强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理spring

因为 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,因此将这部份内容拆分红了四个模块,依次进行分析:websocket

HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

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

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

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

那么本文就接着来分析图中黄色框部分的 AbstractUrlHandlerMapping 系,基于 URL 进行匹配。例如 《基于 XML 配置的 Spring MVC 简单的 HelloWorld 实例应用》 ,固然,目前这种方式已经基本不用了,被 @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(String urlPath, HttpServletRequest request) 方法,得到处理器,详情见下文

  3. 若是找不处处理器,则使用 rootHandlerdefaultHandler 处理器

    1. 若是是/根路径,则使用 rootHandler 处理器
    2. 不然,使用 defaultHandler 默认处理器
    3. 若是找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 做为处理器
    4. 调用validateHandler(Object handler, HttpServletRequest request),对处理器进行校验,空方法,暂无子类实现该方法
    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(String beanName)抽象方法,交由子类实现,详情见 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 较早,可能见过 SimpleUrlHandlerMappingBeanNameUrlHandlerMapping 中的使用示例的配置方式。固然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,仍是使用这种方式。

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

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

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

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

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

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

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

参考文章:芋道源码《精尽 Spring MVC 源码分析》

相关文章
相关标签/搜索