文章目录
0.SpringMVC九大内置组件

1.processRequest方法
1.请求先到service方法
org.springframework.web.servlet.FrameworkServlet#service

2.然后不管是get还是post都会跳转到processRequest方法统一处理
org.springframework.web.servlet.FrameworkServlet#processRequest
java
/**
* 处理 HTTP 请求的核心方法,负责执行 Spring MVC 的核心请求处理流程。
* 该方法由 FrameworkServlet 调用,用于请求的前后处理、异常捕获、异步支持等。
*
* @param request HttpServletRequest 对象,表示客户端的 HTTP 请求
* @param response HttpServletResponse 对象,表示服务器返回的 HTTP 响应
* @throws ServletException 处理 Servlet 相关异常
* @throws IOException 处理 I/O 相关异常
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 记录请求处理的开始时间(用于统计请求耗时)
long startTime = System.currentTimeMillis();
Throwable failureCause = null; // 记录请求处理过程中发生的异常
// 1. 获取当前线程的 LocaleContext(国际化上下文)
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
// 2. 获取当前线程的 RequestAttributes(SpringMVC 请求属性)
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 3. 获取 WebAsyncManager(用于处理异步请求)
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 4. 初始化线程变量(将请求作用域信息存入 ThreadLocal)
initContextHolders(request, localeContext, requestAttributes);
try {
// 5. 调用 doService 进行实际的请求处理(由子类实现,比如 DispatcherServlet)
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. 还原之前的 LocaleContext 和 RequestAttributes,防止线程污染
resetContextHolders(request, previousLocaleContext, previousAttributes);
// 7. 标记请求处理完成,通知所有监听器
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// 8. 记录请求处理结果(用于日志记录)
logResult(request, response, failureCause, asyncManager);
// 9. 发布请求处理完成事件(Spring 事件机制)
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
2.doService方法
org.springframework.web.servlet.DispatcherServlet#doService
java
/**
* 处理 HTTP 请求的核心方法,负责调度 SpringMVC 请求,并将请求转发给 `doDispatch()` 进行处理。
* 该方法由 `FrameworkServlet` 调用,主要职责包括:
* 1. 记录请求日志
* 2. 处理包含(include)请求
* 3. 将 SpringMVC 组件(如 `WebApplicationContext`、`LocaleResolver`)绑定到 `request`
* 4. 处理 FlashMap(用于跨请求传递参数)
* 5. 解析 `RequestPath`
* 6. 调用 `doDispatch()` 进行请求分发
*
* @param request HttpServletRequest 对象,表示客户端的 HTTP 请求
* @param response HttpServletResponse 对象,表示服务器返回的 HTTP 响应
* @throws Exception 可能抛出的异常
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 记录请求日志(用于调试)
logRequest(request);
// 2. 处理包含(include)请求(一般不用)
// 如果请求是 include(如 JSP 的 <jsp: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();
// 只有在 cleanupAfterInclude 为 true 或者 以 `org.springframework.` 开头的属性才会保存
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 3. 将 SpringMVC 相关对象存入 `request`,供 Controller 和 View 使用
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); // 绑定 WebApplicationContext
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); // 绑定 LocaleResolver
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); // 绑定 ThemeResolver
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); // 绑定 ThemeSource(主题源)
// 4. 处理 FlashMap(用于跨请求数据传输,方便重定向时传递参数)
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
// 绑定输入 FlashMap,防止多次修改
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
// 绑定输出 FlashMap(用于存储新传递的数据)
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
// 5. 解析 `RequestPath`
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
// 先保存原始的 RequestPath(如果有的话)
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
// 解析并缓存 RequestPath
ServletRequestPathUtils.parseAndCache(request);
}
try {
// 6. 处理请求:调用 `doDispatch()` 进行请求分发
doDispatch(request, response);
}
finally {
// 7. 如果请求是 include,并且不是异步请求,则恢复原始请求属性
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
// 8. 如果解析了 `RequestPath`,则恢复之前的路径信息
if (this.parseRequestPath) {
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
}
3.doDispatch方法
1.代码
java
/**
* 执行请求分发,调用对应的 `Handler` 处理 HTTP 请求。
* <p>主要流程:
* 1. 确定请求的处理器(Handler)和处理器适配器(HandlerAdapter)
* 2. 处理 `Last-Modified` 头,提高 GET 请求的缓存命中率
* 3. 执行拦截器 `preHandle()`
* 4. 调用 `HandlerAdapter.handle()` 方法,执行控制器(Controller)
* 5. 处理 `ModelAndView`,渲染视图
* 6. 处理异常
* 7. 执行拦截器 `postHandle()` 和 `afterCompletion()`
* 8. 处理异步请求
*
* @param request 当前 HTTP 请求
* @param response 当前 HTTP 响应
* @throws Exception 处理过程中可能抛出的异常
*/
@SuppressWarnings("deprecation")
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request; // 处理后的请求对象
HandlerExecutionChain mappedHandler = null; // 处理器链(包含处理器 + 拦截器)
boolean multipartRequestParsed = false; // 是否解析了 multipart 请求(是否是上传请求)
// 获取 WebAsyncManager(用于管理异步请求)
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null; // 视图模型
Exception dispatchException = null; // 处理过程中可能抛出的异常
try {
// 1. 检查请求是否为 Multipart 类型(如文件上传),并进行解析
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 通过 `HandlerMapping` 获取当前请求对应的 `HandlerExecutionChain`
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response); // 404 处理
return;
}
// 3. 通过 `HandlerAdapter` 获取支持该处理器的适配器(SpringMVC 允许不同的控制器风格)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. 处理 HTTP `Last-Modified` 头,提高 GET 请求的缓存命中率
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; // 直接返回 304 Not Modified
}
}
// 5. 执行拦截器 `preHandle()`,如果返回 false,直接终止请求
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 6. 调用 `HandlerAdapter.handle()`,执行 Controller 方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 7. 检查是否为异步请求,如果是,则不继续执行后续流程
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 8. 处理默认视图名称
applyDefaultViewName(processedRequest, mv);
// 9. 执行拦截器 `postHandle()`,此时 `ModelAndView` 还未渲染
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// 处理 `Handler` 方法中抛出的 `Error`
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 10. 处理请求结果,包括渲染视图和异常处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 11. 触发 `afterCompletion()` 方法,保证拦截器总能执行
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// 12. 如果请求是异步的,则调用拦截器的 `applyAfterConcurrentHandlingStarted()`
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 13. 清理 multipart 请求的资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
2.checkMultipart
java
/**
* 检查当前请求是否是 multipart(多部分)请求,并进行解析。
* <p>如果请求是 multipart 类型(如文件上传),则使用 `MultipartResolver` 进行解析,
* 并将请求转换为 `MultipartHttpServletRequest`,否则返回原始请求。
*
* <p>主要处理逻辑:
* 1. **检查是否配置了 `MultipartResolver`**
* 2. **检查请求是否是 multipart 类型**
* 3. **如果请求已经被解析过,则直接返回**
* 4. **如果解析失败过,则跳过重新解析**
* 5. **尝试解析 multipart 请求**
* 6. **异常处理**
*
* @param request 当前 HTTP 请求
* @return 处理后的请求(如果是 multipart,则返回 `MultipartHttpServletRequest`,否则返回原始请求)
* @throws MultipartException 如果解析 multipart 失败,则抛出异常
* @see MultipartResolver#resolveMultipart
*/
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
// 1. 判断是否配置了 `MultipartResolver`,并且请求是否为 multipart 类型
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
// 2. 如果请求已经是 `MultipartHttpServletRequest`,说明已经解析过,直接返回
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
}
}
// 3. 如果当前请求之前解析 multipart 失败,则跳过重新解析
else if (hasMultipartException(request)) {
logger.debug("Multipart resolution previously failed for current request - " +
"skipping re-resolution for undisturbed error rendering");
}
// 4. 尝试解析 multipart 请求
else {
try {
return this.multipartResolver.resolveMultipart(request);
}
catch (MultipartException ex) {
// 5. 如果请求中已经存在 `ERROR_EXCEPTION_ATTRIBUTE`,说明是错误请求,记录日志后继续处理
if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
logger.debug("Multipart resolution failed for error dispatch", ex);
// 继续使用原始 request 进行处理
}
else {
// 6. 解析失败,抛出异常
throw ex;
}
}
}
}
// 7. 如果请求不是 multipart,或解析失败,则返回原始请求
return request;
}
4.核心流程
