DispatcherServlet分发请求

2023-04-12  本文已影响0人  程序员札记

从 Servlet 规范说起

当 Servlet 容器允许某个 servlet 对象响应请求时,就会调用 Servlet 的 void service(ServletRequest request, ServletResponse response) 方法。

对于选择哪个 servlet 来响应请求的规则,百度 “servlet-mapping url-pattern”,一抓一大把。链接一篇排名靠前的 servlet的url-pattern匹配规则,介绍了四种匹配规则:

下面这张图是 Servlet 容器调用 service 方法运行到 doDispatch 的时序图:

image.png

doDispatch 中跟处理器相关的方法分别是 getHandler,getHandlerAdapter 和 HandlerAdapter # handle

doDispatch 在 HandlerAdapter # handle 前后分别有拦截器相关的方法 HandlerExecutionChain # applyPreHandle,applyPostHandle 和 triggerAfterCompletion

本文重点关注和处理器相关的方法。

getHandler

为当前请求决定一个处理器。

DispatcherServlet # getHandler

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
                // 遍历 DispatcherServlet 中的 HandlerMapping 实例对象列表
        for (HandlerMapping mapping : this.handlerMappings) {
                        // 具体的 HandlerMapping 实例对象调用 getHandler 方法
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

问题1:<mark style="margin: 0px; padding: 0px; box-sizing: border-box;">成员变量 handlerMappings 是怎么来的呢?</mark>

DispatcherServlet # initHandlerMappings


private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;
    if (this.detectAllHandlerMappings) {
                // 找出 DispatcherServlet 和 ContextLoaderListener 上下文中所有实现 HandlerMapping 接口的 Bean
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        Map matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // We keep HandlerMappings in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    else {
                // 找出上下文中,名称为 handlerMapping 且实现 HandlerMapping 的 Bean
        try {
            HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
            this.handlerMappings = Collections.singletonList(hm);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerMapping later.
        }
    }
    // Ensure we have at least one HandlerMapping, by registering
    // a default HandlerMapping if no other mappings are found.
    if (this.handlerMappings == null) {
                // Spring 框架默认的 HandlerMapping
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

阅读源码之后,我们发现初始化 handlerMappings 的方法中,分为自定义初始化多个或者单个 HandlerMapping Bean,如果没有自定义就按照框架默认的来:

  1. 默认检测所有 HandlerMapping Bean:所有实现 HandlerMapping 接口的 Bean

  2. 设置检测唯一 HandlerMapping Bean:名称为 handlerMapping 且实现 HandlerMapping 接口的 Bean。需要对 web.xml 的 <servlet> 做一点修改:

<init-param>
      <param-name>detectAllHandlerMappings</param-name>
      <param-value>false</param-value>
</init-param>

  1. 读取框架默认文件 DipatcherServlet.properties,获取 org.springframework.web.servlet.HandlerMapping 对应的 value。(开发者无法修改)
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

AbstractHandlerMapping # getHandler

这个基类方法是通用的,学习掌握这个方法,基本上就知道 SpringMVC 获取处理器的方法。

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

getHandlerInternal 方法实现目前有 2 种,一种在子类 AbstractUrlHandlerMapping 中,另一种在子类 AbstractHandlerMethodMapping 中。

getHandlerInternal

URL 与 类级别的处理器 映射

AbstractUrlHandlerMapping # getHandlerInternal



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

执行时序图如下:

image.png

URL 与 方法级别的处理器 映射

AbstractHandlerMethodMapping # getHandlerInternal



@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List matches = new ArrayList<>();
    List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // No choice but to go through all mappings...
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }
    if (!matches.isEmpty()) {
        Comparator comparator = new MatchComparator(getMappingComparator(request));
        matches.sort(comparator);
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}
private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
            matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
        }
    }
}

执行时序图如下:

image.png

这两个处理器要详细讲解还得结合实例,这里就不多说了

getHandlerAdapter

通过 getHandler 方法,我们已经得到了“处理器”对象,但是“处理器”对象没有统一的接口,所以使用适配器模式进行统一适配。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
            "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

这里用到了 适配器模式,示意图如下:

image.png

除了 HandlerMethod 以外,其他的几个都是方法的直接转发。右图 HandlerAdapter 虚线框内的类,这是 supports(Object handler) 的目标对象,拿其中一个举例:

@Override
public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
}

所谓的直接转发,看源码:

@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        // 做了一步强转,就把剩下的交给接口方法处理了!
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
}

doDispatch 中执行的“处理器”方法的正是这个 HandlerAdapter # handle(HttpServletRequest request, HttpServletResponse response, Object handler)

总结

doDispatch 的主要流程就是 获取处理器 getHandler, 获取处理器适配器 getHandlerAdapter,执行处理器适配器的 handle 方法

其中,getHandlerInternal 因子类的不同,而有二类不同的行为,一类是 AbstractUrlHandlerMapping,另一类是AbstractHandlerMethodMapping

上一篇下一篇

猜你喜欢

热点阅读