1. Spring mvc 流程
其时序图:

整体流程图:

SpringMVC 工作流程描述:
- 用户向服务器发送请求,请求被Spring 前端控制Servelt
DispatcherServlet捕获; DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)- 提取
Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:- HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
- 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
- Handler执行完成后,向
DispatcherServlet返回一个ModelAndView对象; - 根据返回的
ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet; ViewResolver结合Model和View,来渲染视图- 将渲染结果返回给客户端。
2. HttpServlet & FrameworkServlet
下面我们先看看 DispatcherServlet 的两个父类: HttpServlet 和 FrameworkServlet 。

FrameworkServlet 重写了 HttpServlet 的 service 方法。 我们这里先来看看 FrameworkServlet#service 方法。
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 解析请求方式
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
复制代码
这里我们需要关注两个点 : processRequest(request, response); 和 super.service(request, response); 。下面我们一一来看
2.1 HttpServlet#service
我们知道 HttpServlet 类中分别提供了相应的服务方法( doGet、doPost 等),如下图

同时, HttpServlet 会根据请求的不同形式引导到对应导函数中处理,如下 HttpServlet#service(HttpServletRequest, HttpServletResponse) :
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
// 如果是 get 方法
if (method.equals(METHOD_GET)) {
// lastModified 缓存判断
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
// 如果设置了缓存时间,则判断在ifModifiedSince 之后的时间,是否被修改。
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
}
// 分发不同的请求
else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
复制代码
而这几个函数最常用的就是 doGet() 和 doPost() 。这两个方法被 FrameworkServlet 重写了。我们来看看在 FrameworkServlet 中的实现。
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
...
复制代码
我们可以很清楚的看到,对于大部分的请求,还是依赖于 HttpServlet#service(HttpServletRequest, HttpServletResponse) 来进行一个请求的分发。对于我们常见的 doGet() 和 doPost() 方法都是直接调用 processRequest(request, response); , 而 processRequest 方法 的具体实现在 FrameworkServlet#processRequest 中 。
2.2 FrameworkServlet#processRequest
因此。接下来我们就来看看 org.springframework.web.servlet.FrameworkServlet#processRequest :
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 记录当前时间,用于记录web请求的记录时间
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 1234 的目的是为了保证当前线程的 LocaleContext 和 RequestAttributes 在当前请求后还能恢复,所以提取保存
// 1. 提取当前线程的 LocaleContext 属性
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 2. 根据当前的request 创建对应的 LocaleContext ,并绑定到当前线程
LocaleContext localeContext = buildLocaleContext(request);
// 3. 提取当前线程的 RequestAttributes 属性
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 4. 根据当前的request 创建对应的 RequestAttributes ,并绑定到当前线程
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// todo 5. 委托给 doservice方法进行进一步处理
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
// 6. 请求结束,恢复线程原状
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
// 发布请求结束的通知事件,无论成功与否
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
复制代码
由于逻辑都被封装到 doService(request, response); 中,所以这里还是比较简单。而doService又被 DispatcherServlet 实现了。因此我们这里来看看 DispatcherServlet#doService 。
- DispatcherServlet#doService
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and vi

本文详细剖析了SpringMVC的处理流程,从用户请求到DispatcherServlet,再到HandlerMapping、HandlerAdapter、拦截器的调用,以及视图渲染。重点讨论了doDispatch方法中的checkMultipart、getHandler、getHandlerAdapter、Last-Modified缓存处理和拦截器的各个阶段。通过对源码的分析,揭示了SpringMVC如何优雅地处理HTTP请求。
最低0.47元/天 解锁文章
819

被折叠的 条评论
为什么被折叠?



