前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet处理请求的过程的源码解析。本文是第二篇:DispatcherServlet处理请求的过程的源码解析。
这篇文章能帮你理清楚 JVM、Tomcat、Spring 宏观上的关系。 另外 Servlet ,HttpServlet , ServletRequest, HttpServletRequest 等类,能看到 SpringMVC 对他们的实现与增强,以及SpringMVC处理请求的流程。
回顾整理处理流程
首先让我们整体看一下Spring Web MVC 处理请求的流程:

- 首先用户发送请求------>DispatcherServlet ,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控制;
- DispatcherServlet------>HandlerMapping, HandlerMapping 将会把请求(如 user/list)映射为 HandlerExecutionChain 对象(包含一 个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
- DispatcherServlet------>HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器(适配器设计模型,便于调用),从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器;
- HandlerAdapter------>处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法(Controller 方法),完成功能处理;并返回一个ModelAndView 对象, 前后端分离项目返回的是一个 业务对象(如:List );
- ModelAndView 的逻辑视图名------> ViewResolver,ViewResolver 将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术,**如果是前后端分离项目,此步骤跳过,**没有 ModelAndView 对象,那就没东西解析;
- View------>渲染 ,View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此很容易支持其他视图技术;
- 返回控制权给DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束。
JVM 、 Tomcat 、 Spring宏观关系
JVM 是一个进程,Spring boot是一个库(框架),运行在 JVM 之上,tomcat 是内嵌在 Spring Boot 中的,所以本质上就一个 JVM 进程,Spring Boot 和 tomcat 是两个库。
假如没有Spring Boot , 传统的 tomcat 是需要一个 JVM 进程单独启动,不细说。
tomcat 如何提供网络能力呢?这个库为啥能监听网络请求?
本质上是调用了OS操作系统的 API, 一个请求发送到我们的服务器,最先接收到的是网卡,然后操作系统会把请求的数据放到 socket 接收缓冲区 ,tomcat 具有能够调用操作系统 API 的方法,所以就可以监听操作系统socket缓冲区是否有数据。
java
1. 用户浏览器发送 HTTP 请求
↓
2. 操作系统网络层接收(TCP/IP)
↓
3. Tomcat 通过 JVM 调用操作系统的网络 API
(通常是通过 Java NIO 或传统的 Socket API)
↓
4. Tomcat 解析 HTTP 请求
↓
5. Tomcat 找到对应的 Servlet
↓
6. 调用 Servlet 的方法(如 doGet)
↓
7. 返回响应
聊完了这个我们再看看 Servlet , HttpServlet 和 SpringMVC 的关系。
Servlet , HttpServlet 和 SpringMVC
Servlet 是 JavaEE(Jakarta EE)规范里定义的一个接口, 定义了所有 Web 组件(Servlet)的最基本行为 。 比如最基本的生命周期方法:init()
, service()
, destroy()
。
HttpServlet 是 Servlet
接口的一个抽象实现类,理解成对 Servlet 的能力扩展, 专门为 HTTP 协议做了适配 。
Servlet 有一个方法是 Servlet.service(ServletRequest, ServletResponse) ,Servlet 和 ServletRequest 之间有点像调用关系。没有继承关系或者实现关系。
ServletRequest 它是 java 的一个接口, 封装一次请求的数据(不限于 HTTP,可以是任意协议,比如最早还有 FTP、SMTP 的扩展)。
HttpServletRequest 也是 java 的接口, 继承了 ServletRequest 接口,作为它的能力扩展。新增了 HTTP 特有的方法,比如 getMethod() ------> 获取请求方法(GET/POST/PUT/DELETE ...) , getCookies()
, getSession() 。
以上都是Java规范,接口或者抽象类,Tomcat 对它们都有实现。
tomcat 有 StandardWrapper ,定位和 Servlet 一样,管理容器的生命周期。
tomcat 有 DefaultServlet , 一个具体的 HttpServlet
子类,负责静态资源处理。
tomcat 有 Request 实现了 HttpServletRequest
接口。
关键
已经清晰了 Java 和 Tomcat 的关系,已经完全足够处理请求了,Spring MVC 有什么工作要做呢?
Spring MVC 并没有取代 Servlet,而是构建在 Servlet 之上,提供了一个更强大、更灵活、更易于开发的应用层框架。
我们知道 Spring MVC 核心是 DispatcherServlet
这个组件,它本质上是 HttpServlet 。也就是说 Spring 提供的一个实现了 HttpServlet
接口的类,跟 tomcat 抢活干?对 ,前面流程说了 tomcat 会把请求交给 DispatcherServlet
去跑流程,那DispatcherServlet
就要具有处理请求的能力呀,所以必须要实现 HttpServlet 接口。
Spring MVC 只是实现了 HttpServlet 而已?不需要实现 HttpServletRequest 接口吗?
HttpServletRequest 已经被 tomcat 实现了,Spring MVC 没有改动任何 HttpServletRequest ,拿过来直接用,**但是,**Spring MVC 使用了包装器模式对 HttpServletRequest 进行了增强。你使用的是增强版的HttpServletReqeust ,这个增强对开发者来说是透明的,表明上 HttpServletRequest 没有改变。内部有部分简化和增强。
另外Spring MVC 有一个MultipartHttpServletRequest 接口,实现了 HttpServletRequest 接口,让SpringMVC 处理请求时能接收文件。
最后,SpringMVC提供了一个 RequestContextHolder 工具类,鱼总用了多次,将包装后的 Request 对象 存放在 **ThreadLocal**
(线程上下文) 中,意味着 即使在一个没有直接传入 **HttpServletRequest**
对象的深层 Service 方法里,只要你需要,你也可以通过 **RequestContextHolder.getRequestAttributes()**
来获取到当前请求的上下文信息。
下面我们从源码剖析。
doGet入口
我们启动一个 Spring Boot 项目,端口号为 8080 , 当有一个 get 请求(localhost:8080/api/health) ,Spring MVC 处理过程是怎样的?
- 用户发起 HTTP GET 请求
- Tomcat 接收请求,创建 Request/Response 对象
- Tomcat 调用已注册的 DispatcherServlet.doGet() 方方法。 执行权从 Tomcat 转移到 Spring。
我们就从这个 doGet() 方法开始看源码:
首先doGet()
java
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
- DispatcherServlet.doGet() 调用 processRequest(),执行 Spring 框架通用逻辑,如上下文管理
java
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 计算处理请求的时间
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 初始化context
initContextHolders(request, localeContext, requestAttributes);
try {
// 看这里
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
// 重置context
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
最重要的就是调用doService方法,这个方法由DispatchServlet类实现。
SpringMVC 处理请求的流程
请求分发
- processRequest() 调用 doService(),控制权从通用框架转交给 MVC 专用,doDispatch方法是真正处理请求的核心方法,流程就到了 Spring MVC, 认真看源码,看看Spring MVC 如何处理请求。
java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 判断是不是文件上传类型的request
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 根据request获取匹配的handler.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据handler获取匹配的handlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果handler支持last-modified头处理
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 真正handle处理,并返回modelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 通过视图的prefix和postfix获取完整的视图名
applyDefaultViewName(processedRequest, mv);
// 应用后置的拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理handler处理的结果,显然就是对ModelAndView 或者 出现的Excpetion处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
映射和适配器处理
对于 42 行真正的handle方法,我们看下其处理流程
java
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
交给handleInternal方法处理,以RequestMappingHandlerAdapter这个HandlerAdapter中的处理方法为例
java
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
上面 24 行,打个断点,我们熟悉的 Controller 控制器这这一步触发 ,原来 Spring MVC 中 Handler 处理器概念的技术实现用到了 Controller ,这知识点就接上了我们MVC概念的控制层。控制层会处理数据(Model) , MVC 两个概念都清晰了。当Controller return的时候,会返回 Json 数据给前端,这就属于渲染层了,前后端分离不会真的渲染 HTML 页面给前端了,仅仅是发送JSON数据。

来看看controller 具体怎么被执行
然后执行invokeHandlerMethod这个方法,用来对RequestMapping(usercontroller中的list方法)进行处理
java
**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 重要:设置handler(controller#list)方法上的参数,返回值处理,绑定databinder等
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 执行controller中方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
invokeAndHandle交给UserController中具体执行list方法执行

后续invoke执行的方法,直接看整个请求流程的调用链即可

执行后获得视图和Model

视图渲染
还是那句话,前后端分离没有视图渲染这个步骤,这以 JSP 项目为例讲解的完整 MVC 源码。
接下来继续执行processDispatchResult方法,对视图和model(如果有异常则对异常处理)进行处理(显然就是渲染页面了)
java
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// 如果处理过程有异常,则异常处理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 是否需要渲染视图
if (mv != null && !mv.wasCleared()) {
render(mv, request, response); // 渲染视图
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
接下来显然就是渲染视图了, spring在initStrategies方法中初始化的组件(LocaleResovler等)就派上用场了。
java
/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
后续就是通过viewResolver进行解析了,这里就不再继续看代码了,上述流程基本上够帮助你构建相关的认知了。
最后无非是返回控制权给DispatcherServlet,由DispatcherServlet 返回响应给用户。
最后的最后我们看下请求的日志:
java
21:45:53.390 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - GET "/011_spring_framework_demo_springmvc_war_exploded/user", parameters={}
21:45:53.400 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to tech.pdai.springframework.springmvc.controller.UserController#list(HttpServletRequest, HttpServletResponse)
22:51:14.504 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.view.JstlView - View name 'userList', model {dateTime=Fri Apr 22 21:45:53 CST 2022, userList=[tech.pdai.springframework.springmvc.entity.User@7b8c8dc]}
22:51:14.550 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.view.JstlView - Forwarding to [/WEB-INF/views/userList.jsp]
22:51:44.395 [http-nio-8080-exec-6] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
参考文章
https://pdai.tech/md/spring/spring-x-framework-springmvc-source-2.html