我们在进行web开发中,每次发请求是如何找到哪个方法去处理这个请求的。所谓请求映射的原理就是来查找源码中是如何完成这个处理的。我们知道SpringBoot中每个请求都会来到DispatcherServlet,底层还是使用SpringMVC,所以我们来看DispatcherServlet类。

该类在org.springframework.web.servlet包下:

springboot 请求状态码 设置返回信息 springboot请求原理_sed

进入到该类中,按快捷键“Ctrl+H”查看该类的继承树:

springboot 请求状态码 设置返回信息 springboot请求原理_sed_02

是间接继承了HttpServlet,那么一定会重写doGet()和doPost()方法,而DispatcherServlet类中没有重写doGet()和doPost()方法,那么我们就查看FrameworkServlet类中是否重写了,发现果然重写了,源码如下:

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

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

在两个方法中都调用了processRequest()方法,我们又查看processRequest()方法的源码,该方法的核心就是调用了doService()方法来处理请求和响应:

springboot 请求状态码 设置返回信息 springboot请求原理_sed_03

所以我们继续查看doService()方法源码,是一个抽象方法,那么我们需要看它的子类是否实现了该方法。

protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

我们又回到DispatcherServlet类,发现果然实现了doService()方法,该方法内的核心是调用了doDispatch()方法来处理请求和响应。

springboot 请求状态码 设置返回信息 springboot请求原理_sed_04

调用过程总结如下:请求首先到HttpServlet的doGet()方法,然后这个方法被FrameworkServlet类重写了,在该方法内又调用了该类的processRequest()方法,然后又调用了DispatcherServlet实现的doService()方法,最终交由doDispatcher()方法进行处理。

我们能够在doDispatcher()方法内成功打上断点,我们需要先在控制器类准备一个请求的处理,然后在浏览器访问执行:

@RestController
public class HelloController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello() {
        return "Hello SpringBoot";
    }
}

在doDispatcher()方法的第一行打上断点:

springboot 请求状态码 设置返回信息 springboot请求原理_Express_05

debug模式启动项目,然后在浏览器访问/hello请求

springboot 请求状态码 设置返回信息 springboot请求原理_MVC_06

springboot 请求状态码 设置返回信息 springboot请求原理_MVC_07

让代码走到mappedHandler.getHandler()所在行,这个方法决定哪个handler(controller中的方法)处理当前请求。然后放行,查看handler是什么。

选中mappedHandler = this.getHandler(processedRequest);右键单击出现如图面板,点击Evaluate Expression...计算结果

springboot 请求状态码 设置返回信息 springboot请求原理_Express_08

发现最终要执行的就是HelloController类中的hello()方法

springboot 请求状态码 设置返回信息 springboot请求原理_sed_09

继续将断点打在该行,但是执行到这里强行进入getHandler()方法内,查看如何生成的

springboot 请求状态码 设置返回信息 springboot请求原理_MVC_10

查看handlerMappings的内容有五个处理器映射器

springboot 请求状态码 设置返回信息 springboot请求原理_MVC_11

这里有一个handlerMapping,这是一个处理器映射器,SpringMVC根据它中的映射规则知道了哪个请求应该由哪个处理器(就是我们自己写的控制器类)处理。默认的handlerMapping有5个(如图),其中第一个RequestMappingHandlerMapping中保存了@RequestMapping和handler的映射规则。当我们的应用一启动,SpringMVC自动扫描所有的Controller并解析注解,把注解信息保存在handlerMapping里面。 然后通过循环遍历着5个handlerMapping,看哪个可以处理当前请求。

springboot 请求状态码 设置返回信息 springboot请求原理_Express_12

如上图,发现我们自己在controller中写的路径都存到了Map里面。然后通过mapping.getHandler(request)把请求对应的handler找到(也就是controller中对应的方法)完成映射。

springboot 请求状态码 设置返回信息 springboot请求原理_MVC_13

所有的请求映射都在HandlerMapping中,SpringBoot自动配置了欢迎页的WelcomePageHandlerMapping,访问"/"就能访问到index.html,也自动配置了默认的RequestMappingHandlerMapping,当我们请求进来后,挨个尝试所有的HandlerMapping看是否有请求信息,如果有就找到这个请求对应的handler(就是我们写的控制器),如果没有则继续找下一个HandlerMapping,但如果我们需要一些自定义的映射处理,我们也可以自己通过@Bean给容器中放HandlerMapping。