DispatcherServlet请求处理流程

前言

DispatcherServlet 是 Spring MVC 的核心类,它本质是一个 Servlet,负责接管 HTTP 请求并把它分发给对应的处理器处理,最后处理响应结果渲染页面。
DispatcherServlet 本身并不复杂,它提供了一个模板方法doDispatch()来处理请求,把请求处理的细节交给了依赖的其它组件。
本文先分析 DispatcherServlet 的初始化流程,再分析请求的整体处理流程,至于请求处理中涉及到的其它组件,会另起篇幅。

初始化

首先看一下 DispatcherServlet 的类图:
image.png
DispatcherServlet 本质是一个 Servlet,所以它实现了 Servlet 接口,也就有对应的 Servlet 生命周期方法,在 Servlet 容器启动时,会触发其init()生命周期方法。
image.png
HttpServletBean#init会基于 ServletConfig 配置初始化 bean,Spring 会使用 BeanWrapper 操作 bean 属性,最后调用initServletBean()初始化子类。

public final void init() throws ServletException {
   
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
   
        try {
   
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
   
            if (logger.isErrorEnabled()) {
   
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }
    initServletBean();
}

FrameworkServlet#initServletBean的核心是初始化 WebApplicationContext,然后预留了一个钩子函数initFrameworkServlet()给子类扩展。

protected final void initServletBean() throws ServletException {
   
	this.webApplicationContext = initWebApplicationContext();
	initFrameworkServlet();
}

initWebApplicationContext()用于初始化 Web 容器上下文对象,Spring 首先会在 ServletContext 查找根容器,然后判断 DispatcherServlet 实例化时是否设置上下文对象,如果没有设置则基于ContextClass反射创建上下文对象。

protected WebApplicationContext initWebApplicationContext() {
   
    WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    if (this.webApplicationContext != null) {
   
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
   
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
   
                if (cwac.getParent() == null) {
   
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
   
        wac = findWebApplicationContext();
    }
    if (wac == null) {
   
        wac = createWebApplicationContext(rootContext);
    }
    if (!this.refreshEventReceived) {
   
        synchronized (this.onRefreshMonitor) {
   
            onRefresh(wac);
        }
    }
    if (this.publishContext) {
   
        String attrName = getServletContextAttributeName();
        getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}

上下文对象初始化完成后,最后会触发onRefresh()模板方法完成子类的初始化。对于 DispatcherServlet,主要是初始化一些依赖的组件类,比如:文件上传解析器、处理器映射器、异常处理器、视图解析器等等。

protected void onRefresh(ApplicationContext context) {
   
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
   
    // 多文件上传解析器
    initMultipartResolver(context);
    // 国际化支持
    initLocaleResolver(context);
    // 主题解析器
    initThemeResolver(context);
    // 处理器映射器
    initHandlerMappings(context);
    // 处理器适配器
    initHandlerAdapters(context);
    // 异常处理解析器
    initHandlerExceptionResolvers(context);
    // 请求->视图名 转换
    initRequestToViewNameTranslator(context);
    // 视图解析器
    initViewResolvers(context);
    // 重定向参数传递
    initFlashMapManager(context);
}

组件初始化的逻辑都是一致的,从下文容器查找对应的 Bean、排序并组装 List。
initHandlerMappings()为例:

private void initHandlerMappings(ApplicationContext context) {
   
    this.handlerMappings = null;
    // 是否要在祖先容器检测所有的HandlerMapping
    if (this.detectAllHandlerMappings) {
   
        Map<String, HandlerMapping> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
        if (!matchingBeans.isEmpty()) {
   
            this.handlerMappings = new ArrayList<>(matchingBeans.values());
            // 排序
            AnnotationAwareOrderComparator.sort(this.handlerMappings);
        }
    }
    else {
   
        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.
        }
    }
    // 如果一个都没找到,获取默认策略
    if (this.handlerMappings == null) {
   
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        if (logger.isTraceEnabled()) {
   
            logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
    for (HandlerMapping mapping : this.handlerMappings) {
   
        if (mapping.usesPathPatterns()) {
   
            this.parseRequestPath = true;
            break;
        }
    }
}

至此,DispatcherServlet 就初始化完成了。简单总结一下,因为 DispatcherServlet 本质是一个 Servlet,所以 Servlet 容器启动时会触发它的init()生命周期方法完成初始化。初始化最重要的两个动作,一个是初始化 Web 上下文容器对象、一个是初始化 DispatcherServlet 依赖的各个组件。

处理请求

Servlet 职责是处理请求,Servlet 容器在接收到请求后会调用Servlet#service以响应请求。因为是 HTTP 协议发的请求,所以我们直接看子类 HttpServlet。
为了大家更好的理解,这里画了张流程图:
image.png
HttpServlet 就是单纯的把 ServletRequest 强转成 HttpServletRequest、ServletResponse 强转成 HttpServletResponse,再调用重载方法。

public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
   
    HttpServletRequest  request;
    HttpServletResponse response;
    
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
   
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;

    service(request, response);
}

重载方法里,HttpServlet 根据HttpServletRequest#getMethod把请求拆分成了 doGet、doPost、doPut 等方法交给子类处理。

protected void service(HttpServletRequest req, HttpServletResponse resp){
   
    String method = req.getMethod();
    if (method.equals(METHOD_GET)) {
   
        doGet(req, resp);
    }else if (method.equals(METHOD_HEAD)) {
   
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
    }else if (method.equals(METHOD_POST)) {
   
        doPost(req, resp);
    }
    ......
}

子类 FrameworkServlet 再把拆分后的方法又聚合到一起,统一交给processRequest()处理,做了几件事:

  • 初始化 LocaleContextHolder、RequestContextHolder,把对象写入 ThreadLocal,方便透传
  • 调用模板方法doService()由子类处理
  • 恢复 ContextHolder 上下文对象
  • 发布请求处理事件 ServletRequestHandledEvent
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
   
    // 初始化XXXContextHolder 把对象写入ThreadLocal,方便透传
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    initContextHolders(request, localeContext, requestAttributes);

    try {
   
    	doService(request, response);
    }finally {
   
        // 恢复上下文
        resetContextHolders(request, previousLocaleContext, previousAttributes);
        // 发布处理事件,不管成功与否
        publishRequestHandledEvent(request, response, startTime, failureCause);
    }
}

子类 DispatcherServlet 会把一些需要暴露的对象写入到Request#attributes透传,再把请求实际委托给doDispatch()处理。
doDispatch()是个模板方法,处理请求的过程会依赖很多相关组件,主要流程如下:

  • 检查是不是文件上传请求,是的话就把请求转换成 MultipartHttpServletRequest
  • 查找能处理请求的目标处理器 Handler
  • 查找能协调目标 Handler 干活的适配器 HandlerAdapter
  • 触发拦截器方法:HandlerInterceptor#preHandle
  • HandlerAdapter#handle 适配器协调处理器处理请求
  • 触发拦截器方法:HandlerInterceptor#postHandle
  • 处理响应结果 ModelAndView
  • 触发拦截器方法:HandlerInterceptor#afterCompletion
  • 清理文件上传相关的资源
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;// 是否是文件上传请求
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
   
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
   
            // 检查文件上传请求 Content-Type:multipart/form-data
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 获取处理器
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
   
                noHandlerFound(processedRequest, response);
                return;
            }

            // 获取处理器适配器,协调Handler处理请求
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 处理last-modified,如果资源没发生变更,让客户端用缓存
            String method = request.getMethod();
            boolean isGet = HttpMethod.GET.matches(method);
            if (isGet || HttpMethod.HEAD.matches(method)) {
   
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
   
                    return;
                }
            }
            // 触发 HandlerInterceptor#preHandle
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   
                return;
            }

            // 处理请求
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
   
                return;
            }
            applyDefaultViewName(processedRequest, mv);

            // 触发 HandlerInterceptor#postHandle
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        } catch (Exception ex) {
   
            dispatchException = ex;
        } catch (Throwable err) {
   
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        // 处理结果 对于@ResponseBody接口,只剩触发afterCompletion()
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    } catch (Exception ex) {
   
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    } catch (Throwable err) {
   
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    } finally {
   
        if (asyncManager.isConcurrentHandlingStarted()) {
   
            if (mappedHandler != null) {
   
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else {
   
            if (multipartRequestParsed) {
   
                // 清理文件上传相关的资源
                cleanupMultipart(processedRequest);
            }
        }
    }
}

检查是不是多文件上传请求,交给了组件 MultipartResolver,判断规则也很简单,检查请求头 ContentType 是不是以 “multipart/form-data” 为前缀:

public boolean isMultipart(HttpServletRequest request) {
   
    return StringUtils.startsWithIgnoreCase(request.getContentType(),
            (this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}

如果是文件上传请求,会把请求对象转换成 MultipartHttpServletRequest,可以很方便的遍历上传的文件列表。

public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
   
    return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}

DispatcherServlet#getHandler用来查找目标处理器,依赖组件 HandlerMapping。除了查找 Handler,还会顺带把能应用的拦截器 HandlerInterceptor 一起封装好,构建一个调用链对象 HandlerExecutionChain。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   
    if (this.handlerMappings != null) {
   
        for (HandlerMapping mapping : this.handlerMappings) {
   
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
   
                return handler;
            }
        }
    }
    return null;
}

HandlerMapping#getHandlerAdapter用来查找处理器适配器,在 Spring MVC 里面,处理器 Handler 用 Object 表示,可以以任何形式存在。为了协调 Handler 干活,还必须找到对应的 HandlerAdapter。
DispatcherServlet 初始化会加载容器内所有的 HandlerAdapter 类型的 Bean 封装成 List,查找的过程就是按顺序遍历是否支持目标 Handler,找到了就直接返回。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   
    if (this.handlerAdapters != null) {
   
        for (HandlerAdapter adapter : this.handlerAdapters) {
   
            // 适配器是否支持目标handler
            if (adapter.supports(handler)) {
   
                return adapter;
            }
        }
    }
}

找到了目标 Handler 和 HandlerAdapter,先触发前置拦截器:HandlerInterceptor#preHandle,再调用HandlerAdapter#handle把请求交给目标处理器处理,这一步才是真正的核心,也很复杂,会另起篇幅分析。请求处理完后,触发后置拦截器:HandlerInterceptor#postHandle,最后处理 ModelAndView 渲染视图,触发拦截器:HandlerInterceptor#afterCompletion
最后的最后,整个请求处理完毕后,如果是文件上传请求,还需要调用cleanupMultipart()清理资源。

尾巴

DispatcherServlet 本质是个 Servlet,Servlet 容器启动会触发其init生命周期方法做初始化,初始化最重要的两件事分别是初始化 Web 上下文容器对象、以及依赖的各种解析器,映射器等组件。
Servlet 容器收到请求会调用Servlet#service响应请求,HttpServlet 会根据方法类型把请求拆分成对应的 doXXX 方法由子类处理,FrameworkServlet 再把请求聚合起来,最终由DispatcherServlet#doDispatch处理。这是个模板方法,先检查是不是文件上传请求,再找到目标处理器 Handler、再查找适配器 HandlerAdapter、让 HandlerAdapter 协调 Handler 处理请求,最终处理 ModelAndView 渲染视图。
DispatcherServlet 本身并不复杂,它只是定义了一个处理请求的算法骨架,处理的细节交给了对应的组件类,这也很负责单一职责原则。
通过 DispatcherServlet 我们已经了解了 Spring MVC 处理请求的整体流程,但是请求处理的细节,还需要深入到其依赖的各个组件类。

相关推荐

  1. 简单分析SpringMVC的处理请求流程

    2024-01-07 14:22:03       42 阅读
  2. tomcat处理Http请求流程的步骤

    2024-01-07 14:22:03       44 阅读
  3. tomcat是如何处理Http请求流程的?

    2024-01-07 14:22:03       62 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-07 14:22:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-07 14:22:03       101 阅读
  3. 在Django里面运行非项目文件

    2024-01-07 14:22:03       82 阅读
  4. Python语言-面向对象

    2024-01-07 14:22:03       91 阅读

热门阅读

  1. 数仓工具—Hive进阶之常见的StorageHandler(24)

    2024-01-07 14:22:03       60 阅读
  2. linux内核tcp syn seq读取

    2024-01-07 14:22:03       51 阅读
  3. Mixin用法

    2024-01-07 14:22:03       50 阅读
  4. Unity2D学习笔记 | 《勇士传说》教程 | (五)

    2024-01-07 14:22:03       53 阅读
  5. conda常用命令总结

    2024-01-07 14:22:03       58 阅读
  6. MySQL视图&索引&执行计划&相关十五道面试题分享

    2024-01-07 14:22:03       45 阅读