01-DispatchServlet
DispatchServlet 是 Spring MVC 的核心,其遵循前端控制器设计模式。它是 Spring 实现的一个 servlet,在 servlet 容器中,负责将请求路由到其他的组件当中。
Java ™ Servlet Specification Version 3.0 中对 servlet 和 servlet 容器的定义如下:
A servlet is a Java™ technology-based Web component, managed by a container, that generates dynamic content.
The servlet container is a part of a Web server or application server that provides the network services over which requests and responses are sent, decodes MIME-based requests, and formats MIME-based responses. A servlet container also contains and manages servlets through their lifecycle.
01.1-DispatcherServlet 处理请求的基本流程
[1] 描述了一个请求到达 DispatchServlet 后的基本流程:
请求离开浏览器到达 DispatchServlet
DispatchServlet 负责根据请求携带的 URL 信息,确定请求被路由到哪里
DispatchServlet 将请求转交给 2.中选中的控制器
控制器返回模型和视图名给 DispatchServlet
DispatchServlet 根据视图名确定具体的视图实现
具体的实现将 4.中的逻辑视图渲染成真正的视图
视图被返回给浏览器
了解 DispatchServlet 处理请求的基本流程后,不纠结具体的实现细节,让我们先来看一下在 Spring MVC 中如何配置 DispatchServlet?
01.2-Spring MVC 中配置 DispatcherServlet
通过实现WebApplicationInitializer
接口的方式
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
复制代码
Servlet容器在启动时,会通过SPI机制,回调到`onStartup`方法,具体参考 [Spring MVC 「2」WebApplicationInitializer的工作原理](https://juejin.cn/post/7056695212399132703)。
回到上面的代码,在`onStartup`方法中,我们创建了一个DispatcherServlet,并将其注册在`servletContext`中,并将`/app/*`与其绑定。
复制代码
通过继承AbstractAnnotationConfigDispatcherServletInitializer
类的方式
public class MyDispatchServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
/**
* 仅在需要应用程序上下文需要层次结构时需要
* 如果应用仅需要一个上下文,则可以返回 null,
* 而由getRootConfigClasses返回应用上下文
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}
@Override
protected String[] getServletMappings() {
return new String[0];
}
}
复制代码
通过对`AbstractAnnotationConfigDispatcherServletInitializer`继承层次分析发现,其实现了`WebApplicationInitializer`接口,最终也归结为方式的另类形式,只不过省去了手动实例化DispatcherServlet的麻烦。
![Untitled](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cbb089c7a6143c58ca7c38e91025419~tplv-k3u1fbpfcp-zoom-1.image)
与方式1相比,更推荐方式2,因为使用起来更方便。
复制代码
通过实现WebMvcConfigurer
接口的方式
方式 3 与前面两种方式不太一样,具体不同之处逐个进行分析。
首先,实现WebMvcConfigurer
接口并不能实例化 DispatcherServlet 对象。
其次,WebMvcConfigurer
仅仅是为了让用户可以修改 DispatcherServlet 在处理请求时使用的各种对象,例如拦截器(addInterceptors()
)、视图解析器(configureViewResolvers()
)等等
最后,使用 Spring MVC 时,通常会在配置文件上添加@EnableWebMvc
注解。该注解会通过@Import
引入另一个配置文件org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
,DelegatingWebMvcConfiguration
中定义了setConfigurers
方法:
/**
* 自动从容器中请求所有实现了 WebMvcConfigurer 接口的Bean
* 所以,如果我们自己实现了 WebMvcConfigurer 接口,
* 实现类会在最终添加到 configurers 中
*/
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
复制代码
[1] Spring in Action[2] Specifications
02-结合源码分析 DispatcherServlet 处理请求的具体流程
02.1-DispatcherServlet 初始化过程
当 Servlet 容器启动时,会通过 SPI 机制回调到org.springframework.web.SpringServletContainerInitializer#onStartup
方法。在该方法中会遍历调用类路径下所有实现了WebApplicationInitializer
接口的类的onStartup
方法,最终程序流会进入我们自己的初始化类中。
以 01.2 节中介绍的第 2 中方式为例(即通过继承AbstractAnnotationConfigDispatcherServletInitializer
来初始化 DispatcherServlet) ,程序流会进入到我们的实现类中MyDispatchServletInitializer
。
// 该方法继承自抽象类 AbstractDispatcherServletInitializer
public void onStartup(ServletContext servletContext) throws ServletException {
/**
* 实际上会调用继承自父类 AbstractContextLoaderInitializer 的
* registerContextLoaderListener方法,
* 主要用来创建 rootApplicationContext 和创建监听器
*/
super.onStartup(servletContext);
/**
* 主要用来创建 servletContext 及 DispatcherServlet对象
* 将 DispatcherServlet 对象绑定到某个路径上(由getServletMappings()指定)
*/
this.registerDispatcherServlet(servletContext);
}
复制代码
02.2-一个请求的处理流程
DispatcherServlet 是 Servlet 接口的一个实现,其接口及类实现关系如下图所示:
当请求离开浏览器进入到 tomcat 以后,最终会被 Servlet 容器传递给 DispatcherServlet 继承来的方法HttpServlet#service
中。在service
方法中,根据请求中的 HTTP 方法,会交由特定的方法去处理:
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
复制代码
后续代码以doGet
为例,请求被路由到继承来的FrameworkServlet#processRequest
方法中,然后到达DispatcherServlet#doService
方法中,在设置过一系列属性后(例如),请求最终到达DispatcherServlet#doDispatch
方法中。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// 1. 从handlerMapping中查找处理器(遇到的第一个合适的)
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
// 2. 从适配器中找到第一个匹配的适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
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;
}
// 3. 处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
复制代码
至此,请求的处理过程基本完毕。当然,我们依然忽略了许多的处理细节,这些将在后面的文章中详细分析。
评论