前言:
我们知道 Spring MVC 的核心是前端控制器 DispatcherServlet,客户端所有的请求都会交给 DispatcherServlet 来处理,本篇我我们来分析 Spring MVC 处理客户端请求的流程,也就是工作流程。
Sping MVC 只是储备传送门:
Servlet 和 Spring MVC
是一种服务端程序,主要用于交互式的浏览和修改数据,生成动态 Web 内容,整个过程是客户端发送请求到服务器, 服务器将请求信息发送至 Servlet,Servlet 生成相应内容并将其传给服务器,服务器将响应返回给客户端,传统的 Servlet 技术中,一个接口对应一个 Servlet,每个请求都需要在 web.xml 中配置一个 Servlet 节点,会导致我们开发出许多 Servlet,使用 Spring MVC 可以有效的简化这一步骤,简单来说 Spring MVC 其实就是 Servlet(当前这个说法不够准确)。
Sping MVC 工作流程简图
我们知道 Servlet#service 方法的主要作用是接收客户端发送的 HTTP 请求,并根据请求的类型(GET、POST、PUT、DELETE等)将请求分发到相应的处理器(Controller)进行处理,处理器处理完请求后,将结果返回给 Servlet#service 方法,再由该方法返回给客户端,Servlet#service 方法是由 Spring DispatcherServlet 类实现的,它是整个Spring MVC 框架的核心组件之一。
HttpServlet#service 方法源码分析
Servlet#service、GenericsServlet#service 都是接口方法,我们就从 HttpServlet#service 方法开始分析,HttpServlet#service 方法逻辑十分简单,对 HttpServletRequest 和 ServletResponse 判断后,调用了 FrameworkServlet#service 方法。
//javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//判断是否实现了 HttpServletRequest 和 HttpServletResponse 接口
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
//如果实现了 就强转
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
//调用 FrameworkServlet#service 方法
this.service(request, response);
} else {
//否则抛出异常
throw new ServletException("non-HTTP request or response");
}
}
** FrameworkServlet#service 方法源码分析**
FrameworkServlet#service 方法主要就是对 LocaleContext 和 RequestAttributes 的处理,调用 DispatcherServlet#doService 方法,然后不管是否成功都会发布第二件事就是发布 ServletRequestHandledEvent 事件。
//org.springframework.web.servlet.FrameworkServlet#service
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
//是否是 PATCH 类型
if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
//调用父类方法处理
super.service(request, response);
} else {
//处理请求
this.processRequest(request, response);
}
}
//org.springframework.web.servlet.FrameworkServlet#processRequest
//处理请求
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//获取语言环境
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//给当前请求设置语言环境
LocaleContext localeContext = this.buildLocaleContext(request);
//获取请求属性
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
//绑定到当前请求上
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
//获取异步处理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//注册回调拦截器
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
//将request中最新的 国际化上下文 请求参数 设置到当前线程的上下文中 也是 ThreadLocal
this.initContextHolders(request, localeContext, requestAttributes);
try {
//处理实际请求 调用DispatcherServlet#doService
this.doService(request, response);
} catch (IOException | ServletException var16) {
failureCause = var16;
throw var16;
} catch (Throwable var17) {
failureCause = var17;
throw new NestedServletException("Request processing failed", var17);
} finally {
//还原以前的国际化上下文和请求参数设置到当前线程的上下文中
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
this.logResult(request, response, (Throwable)failureCause, asyncManager);
//发布 ServletRequestHandledEvent 事件
this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
}
}
** DispatcherServlet#doService 方法源码分析**
DispatcherServlet#doService 方法主要就是设置了 request 的一些属性,并对重定向做了一些处理,然后就调用了 DispatcherServlet#doDispatch 方法。
//org.springframework.web.servlet.DispatcherServlet#doService
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//日志记录
this.logRequest(request);
//请求属性备份
Map<String, Object> attributesSnapshot = null;
//是否是 include 请求
if (WebUtils.isIncludeRequest(request)) {
//创建属性快照
attributesSnapshot = new HashMap();
//获取所有属性名称
Enumeration attrNames = request.getAttributeNames();
//开始遍历
label95:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label95;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
//加入属性快照
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
//设置 WebApplicationContext
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
//设置 国际化属性
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
//设置 主题属性
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
//设置 主题源
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
//重定向判断
if (this.flashMapManager != null) {
//重定向管理器不为空 获取重定向参数 同时更新
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
//不为空 设置重定向参数
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
//设置重定向属性参数为空
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
//设置重定向管理器
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
//核心方法 处理请求的方法
this.doDispatch(request, response);
} finally {
//doDispatch 方法执行完后 如果不是异步调用且未完成 对已备份好的快照进行还原 在做完快照后又对 request 设置了一些属性
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
** DispatcherServlet#doDispatch 方法源码分析**
DispatcherServlet#doDispatch 方法是 Spring MVC 的核心方法,其内部流程就是 Spring MVC 处理请求的流程,例如先判断是否是文件上传请求、获取映射器处理器、获取处理器适配器、调用拦截器前处理方法、调用 Handler 处理请求、调用拦截器后处理方法、视图渲染、异步请求的处理等,后面篇章会逐个环节分析。
//org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//request
HttpServletRequest processedRequest = request;
//映射器处理器
HandlerExecutionChain mappedHandler = null;
//是否是文件上传 默认false
boolean multipartRequestParsed = false;
//异步管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
//模型和视图
ModelAndView mv = null;
//异常
Object dispatchException = null;
try {
//文件上传特殊处理
processedRequest = this.checkMultipart(request);
//如果 request 变了 表示有经过特殊处理 也就是说是文件上传请求
multipartRequestParsed = processedRequest != request;
//遍历 HandlerMappings 集合 根据 HandlerMapping 获取 HandlerExecutionChain 即获取映射器处理器
mappedHandler = this.getHandler(processedRequest);
//映射器处理器是否为空
if (mappedHandler == null) {
//为空 没有找到映射器处理器 抛出异常或者返回404
this.noHandlerFound(processedRequest, response);
return;
}
//根据 mappedHandler 获取处理器适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
//获取请求方式
String method = request.getMethod();
//是否是 get 请求
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
//是get 请求或者 head 获取最后修改时间
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
//未修改 且是get 请求 直接返回
return;
}
}
//调用拦截器的preHandle方法 若返回false 处理结束
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//调用handler实际处理请求 获取ModelAndView对象 这里会调用 HandlerAdapter#handle方法处理请求 其内部会调用handler来处理具体的请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//判断异步请求是不是开始了 如果开始就直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果 mv 对象中没有视图 则配置默认视图
this.applyDefaultViewName(processedRequest, mv);
//调用拦截器的 postHandle 方法
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) {
//调用拦截器的afterCompletion方法
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
调用拦截器的afterCompletion方法
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);
}
}
}
本篇简单分析了 Spring MVC 的工作流程,从源码角度分析了一个 Spring MVC 执行一个客户端请求的过程,希望可以帮助大家更好的理解 Spring MVC 的原理。
欢迎提出建议及对错误的地方指出纠正。