美文网首页
DispatcherServlet分发请求

DispatcherServlet分发请求

作者: 程序员札记 | 来源:发表于2023-04-12 22:14 被阅读0次

    从 Servlet 规范说起

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

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

    • 精准匹配(/hello, /admin/home
    • 路径匹配(/hi/*, /hi/kendoizyu/*, /*
    • 扩展名匹配(*.action, *.jsp, *.htm
    • 缺省匹配(/

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

    image.png
    • HttpServlet # service(HttpServletRequest req, HttpServletResponse resp) 主要负责区分 HTTP METHOD,然后分发给 doGet,doPost 等等方法。

    • FrameworkServet # processRequest 负责“请求上下文”的保存和清理。这部分涉及到 ThreadLocal 相关的知识,更多分析可参考 SpringMVC之RequestContextHolder分析

    • FrameworkServet # doService 为 HttpServletRequest 暴露 DispatcherServlet 框架相关的属性。

    • FrameworkServet # doDispatch 为 HttpServletRequest 获取对应的处理器,适配器,执行处理方法,并且包含拦截逻辑。

    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

    相关文章

      网友评论

          本文标题:DispatcherServlet分发请求

          本文链接:https://www.haomeiwen.com/subject/xvnoddtx.html