请求映射原理
我们在进行web开发中,每次发请求是如何找到哪个方法去处理这个请求的。SpringBoot中,每一个请求都会来到DispatcherServlet,底层还是使用SpringMVC,DispatcherServlet是处理所有请求的开始。
DispatcherServlet继承树
DispatcherServlet继承了FrameworkServlet,其中FrameworkServlet中重写了doGet()、doPost()方法
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
...省略...
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
...省略
}
doGet()、doPost()方法又调用了processRequest()方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
...省略...
try {
doService(request, response);
}
...省略...
}
这个方法关键又调用了一个doService()方法去处理,找到这个方法,发现在FrameworkServlet 中并没有实现
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
那就去找子类DispatcherServlet中是否实现了这个方法,发现果然实现了
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
...省略....
logRequest(request);
try {
doDispatch(request, response);
}
...省略....
}
其中关键是doDispatch()方法。
这里总结一下源码调用方法的流程:一个请求过来,首先到HttpServlet的doGet()方法,这个方法被FrameworkServlet重写,进而调用FrameworkServlet中的processRequest()方法,然后调用了DispatcherServlet实现的doService()方法,最后交给doDispatch()方法处理。
doDispatch()方法
我们在debug模式下查看处理流程,首先controller
@RestController
public class HelloController {
Person person;
@RequestMapping("/person")
public Person person(){
return person;
}
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}
浏览器发送一个请求:http://localhost:8080/hello,找到断点处:
我们继续看doDispatch()方法的源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
.....省略一些初始化的内容.....
try {
processedRequest = checkMultipart(request);//直到这个方法,是有功能的方法,判断是不是关于文件上传的请求
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//这个方法决定哪个handler处理当前请求
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
我们让代码走到mappedHandler = getHandler(processedRequest)这一行,这个方法决定哪个handler(controller中的方法)处理当前请求。然后放行,查看handler是什么
那么它是怎么找到的呢,我们把断点打到这个方法上,在重新发送一遍请求
如图,方法参数中的请求确实是我们发送的hello请求,然后step into进入
这里有一个handlerMapping,这是一个处理器映射器,SpringMVC根据它中的映射规则知道了哪个请求应该由哪个处理器处理。默认的handlerMapping有5个(如图),其中第一个RequestMappingHandlerMapping中保存了@RequestMapping和handler的映射规则。当我们的应用一启动,SpringMVC自动扫描所有的Controller并解析注解,把注解信息保存在handlerMapping里面。 然后通过循环遍历着5个handlerMapping,看哪个可以处理当前请求。
如上图,发现我们自己在controller中写的路径都存到了这里面。然后通过mapping.getHandler(request)把请求对应的handler找到(也就是controller中对应的方法)完成映射。
最后,此文章介绍了请求映射的原理,只是个人的一些理解,欢迎指正。