zoukankan      html  css  js  c++  java
  • SpringMVC源代码学习(二)FrameworkServlet内处理请求的流程

    以下内容基于书:《看透SpringMVC-源代码分析与实践》基本照搬。。。用于自己查阅备忘。

    先看一眼DispatcherServlet继承树 
    DispatcherServlet继承树

    我们知道servlet处理方法都是通过HttpServlet的service方法开始,FrameworkServlet重写了父类HttpServlet的service方法。代码如下:

    FrameworkServlet service

    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*
        因为HttpServlet中是没有定义doPatch方法的,所以规定
        if(是doPatch类型)
            就交由本类(FrameworkServlet)内的doPatch方法运行。
        else
            调用父类(HttpServlet)的service方法。
        */
        if (HttpMethod.PATCH.matches(request.getMethod())) {
            processRequest(request, response);
        }
        else {
            super.service(request, response);
        }
    }
    

    HttpServlet的service是根据类型调用不同的do方法。如doGet,doPost。这些方法在FrameworkServlet内被重写了,所以最终调用的还是FrameworkServlet内的相应方法。 
    代码如下(以doGet为例):

    FrameworkServlet(doxxx)

    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
        processRequest(request, response);
    }
    

    我们总结一下这个流程,以doGet为例 
    get request->FrameworkServlet(service)->判断是不是patch请求:不是->HttpServlet(service)->判断是什么类型:get->HttpServlet(doGet)->doGet被子类重写->FrameworkServlet(doGet)->FrameworkServlet(processRequest)

    FrameworkServlet内的其他doxxx请求也是类似的,最终都是processRequest(request, response);来处理,也就是说在HttpServlet的service方法内doxx先按类型分开,然后在FrameworkServlet的doxx合并到一个方法处理。绕了一个大弯

    1. 之所以重新合并,原因还没看到,应该是不同类型的请求在spring内部还需要进行统计处理。
    2. 之所以不直接利用service进行处理,原因书上有讲,主要是出于灵活性的考虑吧,写过代码的同学应该都能理解。

    下面我们就看下processRequest方法,代码如下:

    FrameworkServlet(processRequest)

        protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            long startTime = System.currentTimeMillis();
            Throwable failureCause = null;
            //先获取LocaleContext与RequestAttributes备份起来
            //用于在finally中进行恢复
            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());
            //将当前的localeContext,requestAttributes覆盖进去
            initContextHolders(request, localeContext, requestAttributes);
    
            try {
                //实际处理请求的地方
                doService(request, response);
            }
            catch(...){//catch代码没理解上的意义,被我删掉了
            }
            finally {
                //恢复相关信息,因为在service可能还有Filter等操作
                resetContextHolders(request, previousLocaleContext, previousAttributes);
                if (requestAttributes != null) {
                    requestAttributes.requestCompleted();
                }
                //log...
                //发布Event,类型是ServletRequestHandledEvent,这个下面有解释
                publishRequestHandledEvent(request, response, startTime, failureCause);
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    从细节看, 
    LocaleContextHolder是一个abstract class,没有实现的class,内部的方法都是静态的,用法就是不实例化直接调用,通过它可以获得LocaleContext,也就是语言等本地化信息,如”zh-cn”。 一般获取Locale的方法是request.getLocale(),LocaleContextHolder内的方法因为是静态的,可以随时随地调用,更方便一些。 
    RequestContextHolder也是一样的道理。通过RequestContextHolder可以get到request与session。

    关于发布与监听事件 
    publishEvents设置为true时,请求处理结束后就会发出这个消息,publishEvents在web.xml文件配置SpringMVC的servlet时配置,默认为true。 
    那怎么做到监听这个事件呢,很简单,demo如下:

    //做两件事 1、@Component注解。  2、实现ApplicationListener
    @Component
        public class ServletRequestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent>{
        final static Logger logger = LoggerFactory.getLogger("RequestProcessLog");
        @override
        public void onApplicationEvent(ServletRequestHandledEvent event){
            logger.info(event.getDescription());
        }
    }
    

    doService(request, response); 
    这行语句是实际处理请求的核心语句,它是个抽象方法,交给子类,也就是DispatcherServlet实现。 
    所以我们下篇博客就观察DispatcherServlet的代码。

  • 相关阅读:
    玩转MySQL之Linux下的简单操作(服务启动与关闭、启动与关闭、查看版本)
    玩转MySQL之Linux下修改默认编码
    机器学习算法及应用领域相关的中国大牛
    [转载]Python 包管理工具解惑
    Vim常用操作和快捷键技巧总结
    [转载]那些C++牛人的博客
    [转载]学习c/c++的好网站
    [转载]C++内存管理
    [转载]SQL数据库如何加快查询速度
    [转载]Python3.x和Python2.x的区别
  • 原文地址:https://www.cnblogs.com/fupengpeng/p/7382647.html
Copyright © 2011-2022 走看看