Spring MVC处理请求过程(Spring MVC源码阅读系列之二)

本文是Spring MVC源码阅读系列的第二篇,在上一篇Spring MVC的创建过程中我们介绍了SpringMVC的创建过程,本文讲给主要介绍当一个请求到达时,SpringMVC都做了些什么,即Spring MVC是如何处理请求的。

前言

从上一篇Spring MVC的创建过程 我们知道Spring的HttpServletBean继承于HttpServlet,熟悉Servlet编程的同学都知道我们要编写一个新的Servlet只需继承HttpServlet,并重写相应的方法即可,当一个新的请求到来时,首先从Servlet接口的service方法开始,在HttpServlet的service方法中根据请求的类型不同将请求路由到对应的方法,Spring MVC的HttpServlet继承于Httpservlet类,换句话说Spring MVC也是重写了HttpServlet相应的方法来处理请求,还是内有乾坤,下面我们将遵循由整体到局部的学习方式,先了解Spring MVC处理请求的整体过程。

Spring MVC请求处理整体过程

Spring MVC请求处理的大体过程时请求由DispatcherServlet根据处理器映射确定控制器并分配给它,在控制器完成处理后,请求会被发送给一个根据视图解析器确定的视图来呈现输出结果。如下图:
SpringMVC请求处理整体过程
1. web应用服务器接收到一个新请求是,如果匹配DispatcherServlet的请求映射路径,web容器将该请求转发给DispatcherServlet进行处理
2. DispatcherServlet接收到请求后,将根据请求的信息及HandlerMapping的配置找到处理请求的处理器(Handler)
3. 当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后,通过HandlerAdapter对Handler进行封装,以统一的适配器接口调用Handler
4. 处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和模型数据信息
5. ModelAndView中包含的是“逻辑视图名”,而非真正的视图对象,DispatcherServlet借助ViewResolver完成逻辑视图名到真实视图名对象的解析工作
6. 当得到真实的视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染
7. 最终客户端得到的可能是HTML页面或者其他对象

Spring MVC的请求处理概述

Spring MVC的三个Servlet中的HttpservletBean主要参与了创建工作,没有涉及请求的处理;FrameworkServlet重写了除了doHead的所有处理请求的方法;DispatcherServlet描述了具体处理请求顶层设计结构。
接下来我们先看看FrameworkServlet是如何重写处理请求的方法,又是如何将请求交给DispatcherServlet进行具体的请求处理的。

FrameworkServlet

首先我们先来看看FrameworkServlet的service方法。

protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    String method = request.getMethod();
    //对PATCH类型请求的处理
    if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
        processRequest(request, response);
    }
    else {
        super.service(request, response);
    }
}

从上面的代码我们可以看出,FrameworkServlet在service方法中增加了对PATCH类型请求的处理,其他类型的请求直接交给了父类进行处理,即调用HttpServlet的service方法进行处理。为什么要这样做呢,不再调用super.service(),而是直接将请求交给processRequest处理不是更简单吗?从结构上来看确实如此,但是如果这样做的话会存在一些问题。例如:我们为了某种特殊需求需要在Post请求处理前对request进行一些处理,这时可能会新建一个继承自DispatcherServlet的类,然后覆盖doPost方法,在里面对request进行处理,然后再调用super.doPost方法,但是父类根本没有调用doPost,这时就会出现问题,虽然有其他方法来解决这个问题,但按正常的逻辑,调用doPost应该可以完成才合理,而且一般情况下开发者并不需要对Spring MVC 的内部结构非常了解。所以Spring MVC的这种做法是有必要的。从前言我们已经知道HttpServlet的service方法是根据请求类型的不同将请求路由到不同的处理方法的,那么接下来我们来看看doGet方法(其余方法需要自己处理的方法与doGet类似)

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

我们从上面的代码以及其余的doXXX类型方法中可以发现,这里所做的事情与Httpservle里将不同类型的请求路由到不同方法进行处理的思路正好相反,这里又将所有的请求合并到了processRequest方法中。下面我们将一步步解开这神秘的面纱。首先我们先来看看processRequest方法,其代码如下:

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

    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    //获取LocaleContextHolder中原来保存的LocaleContext
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    //获取当前请求的LocaleContext
    LocaleContext localeContext = buildLocaleContext(request);
    //获取RequestContextHolder中原来保存的RequestAttributes
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    //获取当前请求的ServletRequestAttributes
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    //将当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder中
    initContextHolders(request, localeContext, requestAttributes);

    try {
        //具体处理方法入口
        doService(request, response);
    }
    catch (ServletException ex) {
        failureCause = ex;
        throw ex;
    }
    catch (IOException ex) {
        failureCause = ex;
        throw ex;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值