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的代码。

  • 相关阅读:
    SharePoint 2013 商务智能报表发布
    sharepoint designer web 服务器似乎没有安装microsoft sharepoint foundation
    SharePoint 2013 Designer系列之数据视图
    SharePoint 2013 Designer系列之数据视图筛选
    SharePoint 2013 Designer系列之自定义列表表单
    SharePoint 2013 入门教程之创建及修改母版页
    SharePoint 2013 入门教程之创建页面布局及页面
    SharePoint 2010 级联下拉列表 (Cascading DropDownList)
    使用SharePoint Designer定制开发专家库系统实例!
    PL/SQL Developer 建立远程连接数据库的配置 和安装包+汉化包+注册机
  • 原文地址:https://www.cnblogs.com/fupengpeng/p/7382647.html
Copyright © 2011-2022 走看看