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

  • 相关阅读:
    【POJ 3162】 Walking Race (树形DP-求树上最长路径问题,+单调队列)
    【POJ 2152】 Fire (树形DP)
    【POJ 1741】 Tree (树的点分治)
    【POJ 2486】 Apple Tree (树形DP)
    【HDU 3810】 Magina (01背包,优先队列优化,并查集)
    【SGU 390】Tickets (数位DP)
    【SPOJ 2319】 BIGSEQ
    【SPOJ 1182】 SORTBIT
    【HDU 5456】 Matches Puzzle Game (数位DP)
    【HDU 3652】 B-number (数位DP)
  • 原文地址:https://www.cnblogs.com/fupengpeng/p/7382647.html
Copyright © 2011-2022 走看看