SpringMVC请求执行流程源码解析

文章目录

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.核心流程

相关推荐
S-X-S5 天前
SpringMVC环境搭建
springmvc
S-X-S23 天前
装饰SpringMVC的适配器实现响应自动包装
java·springmvc·适配器·web响应
极客先躯1 个月前
高级java每日一道面试题-2025年01月24日-框架篇[SpringMVC篇]-SpringMVC常用的注解有哪些?
java·springmvc·常用的注解
不修×蝙蝠1 个月前
SpringMVC(三)请求
spring·springmvc·请求·springmvc请求
mikey棒棒棒1 个月前
SSM-SpringMVC
java·ssm·springmvc
不修×蝙蝠2 个月前
SpringMVC(二)原理
spring·springmvc·原理·spring框架·配置maven
不修×蝙蝠2 个月前
SpringMVC(一)配置
java·spring·ssm·springmvc·配置
不修×蝙蝠2 个月前
SpringMVC(六)拦截器
spring·ssm·springmvc·拦截器·登录管理
Ttang232 个月前
SpringMVC(1)——SpringMVC配置和基本原理
java·spring·springmvc