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的麻烦。

与方式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); }
}}
复制代码
至此,请求的处理过程基本完毕。当然,我们依然忽略了许多的处理细节,这些将在后面的文章中详细分析。
评论