浅析Spring Boot请求映射原理
Spring Boot的底层还是Spring MVC,那么Spring Boot的请求映射就与Spring MVC是相同的,那就是根据DispatcherServlet完成了请求映射的功能。但是我们没有手动去配置DispatcherServlet对应的配置项,那么Spring Boot是如何加载的DispatcherServlet呢,这个答案就是之前讲解的Spring Boot自动装配。
那么首先来看看自动装配的DispatcherServlet
测试程序
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/hello")
public String hello() {
return "hello world!";
}
}
加载DispatcherServlet
之前分析Spring Boot自动装配原理时可以发现,Spring Boot是通过扫描所有引入的jar包中的spring.factories文件来获取自动配置项的,那么DispatcherServlet的自动配置也会通过这种方法引入。我们可以在spring-boot-autoconfigure-2.3.4.RELEASE.jar包下的META-INF/spring.factories中找到这个自动配置类:DispatcherServletAutoConfiguration。
在这个自动配置类中,会判断当前应用是否为SERVLET类型的web应用,是的话才会继续导入DispatcherServlet。
当满足上述的条件后,还会再进行其他的判断:
只有DefaultDispatcherServletCondition和ServletRegistration(接口)这两个类存在时才会加载DispatcherServlet,并且通过WebMvcProperties为DispatcherServlet绑定了相应的配置项:
可以看出这个配置是以spring.mvc作为前缀的配置,那么它都包含哪些配置呢:
通过WebMvcProperties类或者application.yml文件我们可以发现它的可配置项极多,这些配置项的作用不是请求映射的关键,这里不再展开讲解。
当满足这一切条件之后,spring boot会为我们new一个dispatcherServlet对象,并进行相应的配置,最后将其放到spring容器中交给容器进行管理。这就是spring的DispatcherServlet的自动装配原理。
DispatcherServlet请求映射
我们可以发现DispatcherServlet是一个Servlet,并且继承自HttpServlet,当我们自己实现HttpServlet类时,通常需要自己实现doGet和doPost方法。
通过源码可以发现,这里HttpServletBean并没有实现doGet和doPost方法,但是在FrameworkServlet中实现了这两个方法:
最终它们调用了processRequest方法进行请求的处理:
这个方法在进行了一系列的初始化操作以后,调用了doService方法,doService方法在FrameworkServlet类中是一个抽象方法,所以这个方法的具体实现是在DispatcherServlet中具体实现的。
在该方法中,除了设置了一系列的属性以外,调用了doDispatch方法进行了请求的分派,这个方法就是请求映射的关键之处。
doDispatch方法
从源码中我们可以发现,doDispatch方法首先通过getHandler方法获取到了处理请求的handler,然后通过调用这个handler来处理前端传递的请求。
调用handler处理前端传递的请求。
那么首先来看看getHandler方法。
getHandler
可以看出这个方法通过存储在HandlerMapping中的handler选择匹配当前请求的handler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
// org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
此时总共handlerMappings中共包含六个handlerMapping:
此时可以发现所有的请求都存储在RequestMappingHandlerMapping中:
接着会调用AbstractHandlerMapping#getHandler方法获取HandlerExecutionChain,我们继续深入进去看看它们是如何进行匹配的,通过调试过程中的函数调用我们可以发现匹配发生在getHandlerInternal方法中: