Spring MVC 运行流程解析(含源码分析)

章节目录

  • Spring MVC DispatcherServlet 与 HttpServlet 关系类图
  • Spring MVC 源码分析Request 请求映射、执行、视图解析流程
  • 总结-Spring MVC 运行流程图

1.Spring MVC DispatcherServlet 与 HttpServlet 关系类图

1.1 什么是DispatcherServlet
源码注释如下所示:

Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities.

译文如下:

DispatcherServlet 是HTTP请求处理程序/控制器的中央调度程序(将请求映射到具体处理器(handler)上 ),例如用于Web UI控制器或基于HTTP的远程服务导出器(webService),调度器会将请求路由至已经注册好的具体的hadler,使得handler可以处理执行相关的web请求,提供了请求与处理器之间的映射关系功能,其实就是路由映射功能。

1.2 什么是HttpServlet

HttpServlet 是处理相关基于Http请求的处理程序,请求的相关信息被封装成 HttpServletRequest对象,其中Service() 方法通过获取 HttpServletRequest 中的方法名 如 GETPOSTPUT等 request-method信息的获取,去invoke具体的doGet()doPost()doPut()方法,最终将执行完业务逻辑获取到的处理数据通过HttpServletResponse对象返回给客户端。所以最终request请求结果还是从HttpServlet中的service()返回的

那么这两者之间有什么关系呢?
如下图所示DispatcherServlet与HttpServlet之间的类图关系:

《Spring MVC 运行流程解析(含源码分析)》

其中最重要的是FrameworkServlet。

源码注释如下:

Base servlet for Spring’s web framework. Provides integration with
a Spring application context, in a JavaBean-based overall solution.

译文如下:

Spring web 框架中的基础Servlet,将Spring 相关的ApplicationContext 集成进来。方便我们在后期使用Spring IOC 容器中注册的各种属性的类对象。

FrameworkServlet 整合Spring WebApplicationContext 对象源码如下:

    /**
     * Initialize and publish the WebApplicationContext for this servlet.
     * <p>Delegates to {@link #createWebApplicationContext} for actual creation
     * of the context. Can be overridden in subclasses.
     * @return the WebApplicationContext instance
     * @see #FrameworkServlet(WebApplicationContext)
     * @see #setContextClass
     * @see #setContextConfigLocation
     */
    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
    ...
 return wac;
}

其中获取web应用程序上下文的代码段为:

WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());

FrameworkServlet 对 extends 自 HttpServlet 的service()方法进行了override()

《Spring MVC 运行流程解析(含源码分析)》 override service()

super.service()即调用HttpServlet中的Service()方法
可以看到Service()方法根据request.method 去调用具体的doxxx()方法,这里FrameworkServlet 对 doxxx()方法也进行了override()。

如下为FrameworkServlet 中doGet()方法源码

《Spring MVC 运行流程解析(含源码分析)》 override doGet()

其中的processRequest()方法源码如下所示:

    /**
     * Process this request, publishing an event regardless of the outcome.
     * <p>The actual event handling is performed by the abstract
     * {@link #doService} template method.
     */
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;

        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);
        }
        catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }

            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }
  • 其中最重要的是doService()方法,这个doService()方法被声明为抽象方法,在DispatcherServlet 做具体实现。
    源码实现如下:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                    " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        // Keep a snapshot of the request attributes in case of an include,
        // to be able to restore the original attributes after the include.
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<String, Object>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }
                //为请求设置具体的属性。
        // Make framework objects available to handlers and view objects.
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
                        //调用doDispatch(),将请求分配给具体的handler去处理。实际上第二节会具体分析doDispatch()方法
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

1.3 Spring Web 对Request的执行流程
所以请求的整个执行流程依据之前的HttpServlet知识积累(未debug)可以大致总结如下(第二节会debug源码,用来验证我们总结的这个流程)
即:

1.请求到达dispatcherServlet,(非初次请求,初次请求会涉及dispatcherServlet初始化,调用init()方法)。
2.dispatcherServlet 执行service()方法,因Dispatcher类继承FrameworkServlet,所以调用父类的service()方法。
3.service()调用FrameworkServlet 中具体的doxxx()方法
4.FrameworkServlet 具体的 doxxx()方法调用processRequest()方法。
5.processRequest()方法调用dispatcherServlet 的 doService()方法。
6.dispatcherServlet 的 doService()方法调用doDispatch()方法。

注意:上述根方法-service()方法被servlet容器 Servlet container显示调用。

2.Spring MVC 源码分析Request 请求映射、执行、视图解析流程

简单的helloword级别的web项目,搭建方式可以略过。主要是debug开始的地方我们需要确定,因为有HttpServlet源码分析的积累,那么我们直接在DispatcherServlet中的Service方法中打断点就可以了,因为DispatcherServlet继承了FrameworkServlet,FrameworkServlet对HttpServlet中的service()方法进行了override,所以程序入口断点应该打在FrameworkServlet 中的service() 方法,接下来就是实操演示:

注意:本源码分析的是Spring 4.1版本

2.0 debug的目的

了解 request 到具体 handler 的执行流程。

2.1 FrameworkServlet 中 service() 打断点

《Spring MVC 运行流程解析(含源码分析)》 2

2.2 开启debug模式

《Spring MVC 运行流程解析(含源码分析)》 image.png

2.3 开始debug

《Spring MVC 运行流程解析(含源码分析)》 image.png

执行父类service()方法

《Spring MVC 运行流程解析(含源码分析)》 image.png

执行doGet()方法

《Spring MVC 运行流程解析(含源码分析)》 image.png

执行processRequest()方法

《Spring MVC 运行流程解析(含源码分析)》

执行doService()方法

《Spring MVC 运行流程解析(含源码分析)》

执行doDispatch方法

《Spring MVC 运行流程解析(含源码分析)》

获取请求对应的handler

《Spring MVC 运行流程解析(含源码分析)》

继续debug hm.getHandler(request)看看这其中发生了什么?
通过SimpleUrlHandlerMapping, 发现并不能获取到 对应的 handler(HandlerExcutionChain对象),
继续foreach

《Spring MVC 运行流程解析(含源码分析)》

通过EndpointHandlerMapping, 发现并不能获取到 对应的 handler(HandlerExcutionChain对象),

继续foreach

《Spring MVC 运行流程解析(含源码分析)》 image.png

《Spring MVC 运行流程解析(含源码分析)》 image.png

最终我们通过RequestMappingHandlerMapping对象获取到了对应的handler对象。
可以看下handler对象是什么东东?

《Spring MVC 运行流程解析(含源码分析)》 image.png

所以handlerExcutionChain 对象 包含有handler对象、interceptor对象。

到此我们通过requestMappingHandlerMapping 获取到了请求对应的handler。

《Spring MVC 运行流程解析(含源码分析)》 image.png

接下来需要以handler为参数获取真正处理请求的handlerAdaptor

《Spring MVC 运行流程解析(含源码分析)》 image.png

接下来执行 handlerAdaptor 中 handler()方法

《Spring MVC 运行流程解析(含源码分析)》 image.png

返回mv,需要注意的是,返回mv 其实是对Controller 中业务方法的调用其实使用到了反射。

《Spring MVC 运行流程解析(含源码分析)》 image.png
《Spring MVC 运行流程解析(含源码分析)》 image.png
《Spring MVC 运行流程解析(含源码分析)》 image.png

注意在返回mv之前 通过handlerExcutionChain对象可以调用applyPreHandler 方法,可以在返回mv之前做预先处理工作。

返回mv之后,可以通过handlerExcutionChain对象可以调用applyPreHandler 方法对返回的mv做修改。我们只需要实现 handlerInterceptor类并实现配置就可以了。

最后一步执行视图渲染的工作,这一步是在dispatcherServlet中完成的。

《Spring MVC 运行流程解析(含源码分析)》

最终请求结果

《Spring MVC 运行流程解析(含源码分析)》

注意:由于返回结果为String 类型的value,不涉及视图解析,所以render 方法并没有执行。

3.总结-Spring MVC 运行流程图

《Spring MVC 运行流程解析(含源码分析)》 image.png
《Spring MVC 运行流程解析(含源码分析)》 image.png

对上述流程图的解释:

  • 用户发起请求到前端控制器(Controller)
  • 前端控制器没有处理业务逻辑的能力,需要找到具体的模型对象处理(Handler),到处理器映射器(HandlerMapping)中查找Handler对象(Model)。
  • HandlerMapping返回执行链,包含了2部分内容: ① Handler对象、② 拦截器数组
  • 前端处理器通过处理器适配器包装后执行Handler对象。
  • 处理业务逻辑。
  • Handler处理完业务逻辑,返回ModelAndView对象,其中view是视图名称,不是真正的视图对象。
  • 将ModelAndView返回给前端控制器。
  • 视图解析器(ViewResolver)返回真正的视图对象(View)。
  • (此时前端控制器中既有视图又有Model对象数据)前端控制器根据模型数据和视图对象,进行视图渲染。
  • 返回渲染后的视图(html/json/xml)返回。
  • 给用户产生响应。
    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/u012780655/article/details/83353302
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞