SpringMVC之三个Servlet

三个Servlet

一、HttpServletBean

参与创建工作,没有涉及请求的处理

二、FrameworkServlet

当第一个请求过来时,就初始化容器

init.png

如上图,当第一个请求过来时,FrameworkServlet就初始化容器,并将Spring的容器进行刷新,而DispatcherServlet的onRefresh刷新只是初始化九大组件。以后的请求就都不需要初始化容器。
初始化完容器以后,因为FrameworkServlet重写了Servlet的service,doGet,doPost等方法,所以会先走到Framework的doGet或者doPost等请求方法。
FrameworkServlet的设计很有趣,它将所有的请求get,post,delete等请求全部嫁接到processRequest(request,response)
processRequest.png

processRequest(request,response)方法中又调用了doService(request,response)方法,该方法是在DispatcherServlet中具体实现
所以说FrameworkServlet主要有两个作用:
1.创建并初始化Spring容器
2.请求的入口

三、DispatcherServlet

第一个作用是初始化九大组件,就是在上面的FrameworkServlet执行完初始化容器后,就调用onRefresh,方法里面只调用了initStrategies(context)方法,initStrategies方法才是重点,如下图

nine_components.png

每个组件在处理请求时都有可能会用到

第二个作用就是处理请求
请求的入口是doService ,我们看下请求过来的调用链

image.png

doService对request设置了一些属性
doService.png

接着调用doDispatchdoDispatch是DispatcherServlet的核心
我们看代码

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        /**
         *
         *   doDispatch中最重要的代码总共有四句(见下面的标签数字)
         *
         *   Handler、HandlerMapping、HandlerAdapter三者的关系:
         *      Handler:就是我们的控制器Controller中加了@XXXMapping的方法
         *      HandlerMapping: 用来快速查找Handler
         *      HandlerAdapter:调用Handler来干活,而且不同Handler需要不同的Adapter
         *   这就好比HandlerAdapter是工人,Handler是工具,HandlerMapping是根据加工的需求来选择用
         *   什么设备
         */

        /**
         * 封装Request,如果不是上传请求则直接使用接收到的request
         * 如果是上传请求,重新封装成MultipartHttpServletRequest
         */
        HttpServletRequest processedRequest = request;
        /**
         * 处理请求的处理器链
         * 包含有处理器Handler和对应的拦截器Interceptor
         */
        HandlerExecutionChain mappedHandler = null;
        /**
         * 是否为上传请求的标记
         */
        boolean multipartRequestParsed = false;

        /**
         * 从request中获取异步请求
         */
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            /**
             * View 跟 ViewResolver
             * View 是用来展示数据的
             * 而ViewResolver是用来查找View的
             * 做完请求工作后,需要返回结果,而返回结果就需要模板,
             * View就是所需要的模板,ViewResolver就是来选择哪个模板
             *
             * **/
            ModelAndView mv = null;
            /**
             * 异常声明
             * doDispatch()中对异常又两种处理方法:
             *      一、如果是处理请求中出现的异常,会捕获并在processDispatchResult中渲染到最后的视图中
             *      二、如果是渲染中出现异常,则直接抛出
             */
            Exception dispatchException = null;

            try {
                // 检查是不是上传请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                /** 第一句
                 *  使用HandlerMapping找到可以干活的Handler
                 *
                 * **/
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 找不到Handler返回404
                    noHandlerFound(processedRequest, response);
                    return;
                }

                /** 第二句
                 *  找到合适的HandlerAdapter去让他干活
                 * **/
                // Determine handler adapter for the current request.
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 处理GET、HEAD请求的last-modified
                // Process last-modified header, if supported by the handler.
                /**
                 * Last-Modified是HTTP头部的一种属性,表示当前请求的资源跟上一次请求的资源是否相同
                 * 如果相同,返回304并且没有新的实体(body)返回
                 * 否则返回新的实体内容
                 *
                 * 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是客户端请求的资源,
                 * 同时有一个Last-Modified的属性标记此文件在服务器端最后被修改的时间。
                 * 客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头,询问该时间之后文件是否有被修改过
                 * 如果服务器端的资源没有变化,则自动返回 HTTP 304(Not Changed.)状态码,内容为空,这样就节省了传输数据量。
                 * 当服务器端代码发生改变或者重启服务器时,则重新发出资源,
                 * 返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
                 */
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                /** 如果有拦截器,就饿执行我们的拦截器,preHandle前置处理**/
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                /** 第三句
                 *  让HandlerAdapter开始干活,干完活后返回数据
                 * **/
                // Actually invoke the handler.
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 如果需要异步处理,则直接返回
                /**
                 * 因为异步处理会重新开启一个线程去执行结果的返回
                 * 不会占用目前这个线程,所以可以直接返回
                 */
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                // 当view为空时(比如,handler返回类型为void),根据request设置默认view
                applyDefaultViewName(processedRequest, mv);
                /** 执行了拦截器的后置处理 postHandle**/
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            /** 第四句
             *  将数据处理,通过View展示给用户
             *  处理结果,包括处理异常,渲染页面,发出完成通知,触发拦截器的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()) {
                // 执行异步的拦截器
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // 删除上传请求的资源,不然会产生临时的资源
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

doDispatch方法主要有四个任务:1.用request找到Handler; 2.根据Handler找到HandlerAdapter; 3.用HandlerAdapter处理Handler 4.调用processDispatchResult方法处理结果(包括异常的处理和View视图的渲染)。
除此之外还另外做了许多事情,比如判断是否为上传文件请求,是否带有Last-Modified头部,执行拦截器,是否为异步请求...
processDispatchResult方法处理上面doDispatch返回的结果,包括处理异常、渲染页面、触发拦截器的后置处理,处理异常只是处理doDispatch()方法中的异常、渲染页面的异常就抛出。渲染页面的方法调用render

你可能感兴趣的:(SpringMVC之三个Servlet)