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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容