SpringMVC源码-SpringMVC源码请求执行流程及重点方法doDispatch讲解

一、开始请求

在浏览器访问http://localhost:8080/spring_mymvc/userlist 这个接口,是个get请求。

FrameworkServlet类的service方法会被请求到:

调用路径如下:

bash 复制代码
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

可以看到该请求时get请求,走下面分支处理。进入super.service(request, response);发现方法跟丢了。。。。。。。

因为源码时在tomcat里的

super.service(request, response);Ctrl加鼠标左键点进去发现,进入到HttpServlet类的service方法:

会进入到FrameworkServlet的doGet方法,然后走processRequest(request, response);最后执行doService(request, response);方法

FrameworkServlet #doGet 执行路径:

bash 复制代码
doGet:960, FrameworkServlet (org.springframework.web.servlet)
service:655, HttpServlet (javax.servlet.http)
service:945, FrameworkServlet (org.springframework.web.servlet)
service:764, HttpServlet (javax.servlet.http)
internalDoFilter:227, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:189, ApplicationFilterChain (org.apache.catalina.core)
doFilter:162, ApplicationFilterChain (org.apache.catalina.core)
invoke:197, StandardWrapperValve (org.apache.catalina.core)
invoke:97, StandardContextValve (org.apache.catalina.core)
invoke:541, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:135, StandardHostValve (org.apache.catalina.core)
invoke:92, ErrorReportValve (org.apache.catalina.valves)
invoke:687, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:360, CoyoteAdapter (org.apache.catalina.connector)
service:399, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:890, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1789, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

可以看到无论git还是post请求最终都会执行processRequest(request, response);方法

因为不管是什么请求方式除了参数的处理不一样 其他的都有相似之处 所以都执行一个最终的公共方法。
processRequest

bash 复制代码
/**处理此请求,发布一个事件,而不管结果如何。<p>实际的事件处理是由抽象的{@link doService}模板方法执行的。
	 * 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;

		// 获取LocaleContextHolder中原来保存的LocaleContext(保存的本地化信息)  事务中就是这样 先把最开始的保存 把新的放进去 用完新的再把最开始保存的恢复回去
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		// 获取当前请求的LocaleContext
		LocaleContext localeContext = buildLocaleContext(request);

		// 获取RequestContextHolder总原来保存的RequestAttribute(管理request和session的属性) spring事务处理的时候 有类似的操作 获取当前的 保存起来 后续恢复
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		// 获取当前请求的ServletRequestAttribute
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		// 获取异步管理器
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		// 将当前请求的LocaleContext和ServletRequestAttribute设置到LocaleContextHolder和RequestContextHolder
		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 {
			// 恢复原来的LocaleContext和ServletRequestAttributes到LocaleContextHolder和RequestContextHolder中
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			// 如果日志级别为debug,则打印请求日志
			logResult(request, response, failureCause, asyncManager);
			// 发布ServletRequestHandledEvent请求处理完成事件
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

执行真正的逻辑
doService(request, response);

java 复制代码
/**公开dispatcherservlet特定的请求属性,并委托给{@link doDispatch}进行实际的调度。
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 如果日志级别为 DEBUG,则打印请求日志
		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.
		// 当include请求时对request的Attribute做快照备份
		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 view objects.
		// 设置Spring框架中的常用对象到request属性中,这四个属性会在handler和view中使用
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
         //重定向的时候方便参数的传递
		// FlashMap的相关配置,主要用于Redirect转发时参数的传递,此处有一个应用场景:如果post请求是提交表单,提交完之后redirect到一个显示订单的页面,
		// 此时需要知道一些订单的信息,但redirect本身没有提交参数的功能,如果想传递参数,那么就必须要写到url,而url有长度的限制同时还容易对外暴露,此时
		// 可以使用flashMap来传递参数,
		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 {
			// 执行请求的分发
			doDispatch(request, response);
		}
		finally {
			// 异步处理相关
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				// 还原request快照的属性
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

执行请求的分发

doDispatch(request, response);

java 复制代码
/**
	 * 处理实际的分发到处理器中
	 * 内层是捕获在对请求进行处理的过程中抛出的异常,在处理异常的时候会设置到dispatcherException变量,然后在processorDispatcherResult方法中进行处理
	 * 外层是处理渲染页面时抛出的异常,主要是处理processDispatchResult方法抛出的异常
	 *
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 实际处理时所用的request,如果不是上传请求,则直接使用接收到的request,否则封装成上传类型的request
		HttpServletRequest processedRequest = request;
		// 处理请求的处理器链(包含处理器和对应的interceptor)
		HandlerExecutionChain mappedHandler = null;
		// 是不是上传请求的标志
		boolean multipartRequestParsed = false;

		// 获取异步管理器
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
          //这里划为两个try ,第一个是处理请求后端,第二个是处理数据渲染。分别处理异常了
		try {
			// 封装model和view的容器
			ModelAndView mv = null;
			// 处理请求过程中抛出的异常,但是不包含渲染过程中抛出的异常
			Exception dispatchException = null;

			try {
				// 检测请求是否为上传请求,如果是则通过multipartResolver将其封装成MultipartHttpServletRequest对象
				processedRequest = checkMultipart(request);
				// 设置上传请求的标志
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 获得请求对应的HandlerExecutionChain对象(HandlerMethod和HandlerInterceptor拦截器们)
				mappedHandler = getHandler(processedRequest);//请求对应的是哪个controller
				//  如果获取不到,则根据配置抛出异常或返回404错误
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 获得当前handler对应的HandlerAdapter对象 controller或者控制器有多种不同的实现方式 为了方便后续过程中调用 使用适配器模式来 解决
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				// 处理GET、HEAD请求的Last-Modified,当浏览器第一次跟服务器请求资源时,服务器会在返回的请求头里包含一个last_modified的属性,
				// 代表资源最后时什么时候修改的,在浏览器以后发送请求的时候,会同时发送之前接收到的Last_modified.服务器接收到带last_modified的请求后,
				// 会跟实际资源的最后修改时间做对比,如果过期了返回新的资源,否则直接返回304表示未过期,直接使用之前缓存的结果即可
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					// 获取请求中服务器端最后被修改时间
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// 执行响应的Interceptor的preHandler
				// 注意:该方法如果有一个拦截器的前置处理返回false,则开始倒序触发所有的拦截器的 已完成处理
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 真正的调用handler方法,也就是执行对应的方法,并返回视图
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// 如果需要异步处理,直接返回
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				// 当view为空时,根据request设置默认的view
				applyDefaultViewName(processedRequest, mv);
				// 执行响应的interceptor的postHandler方法
				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);
			}
			// 处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion
			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);
				}
			}
		}
	}

重要的方法:

  • 检测请求是否为上传请求,如果是则通过multipartResolver将其封装成MultipartHttpServletRequest对象
    processedRequest = checkMultipart(request);
  • 获得请求对应的HandlerExecutionChain对象(HandlerMethod和HandlerInterceptor拦截器们)
    mappedHandler = getHandler(processedRequest);//请求对应的是哪个controller
  • 获得当前handler对应的HandlerAdapter对象 controller或者控制器有多种不同的实现方式 为了方便后续过程中调用 使用适配器模式来 解决
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  • 处理GET、HEAD请求的Last-Modified,当浏览器第一次跟服务器请求资源时,服务器会在返回的请求头里包含一个last_modified的属性,
    // 代表资源最后时什么时候修改的,在浏览器以后发送请求的时候,会同时发送之前接收到的Last_modified.服务器接收到带last_modified的请求后,
    // 会跟实际资源的最后修改时间做对比,如果过期了返回新的资源,否则直接返回304表示未过期,直接使用之前缓存的结果即可
    lastModified
  • 执行响应的Interceptor的preHandler
    // 注意:该方法如果有一个拦截器的前置处理返回false,则开始倒序触发所有的拦截器的 已完成处理
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
  • 真正的调用handler方法,也就是执行对应的方法,并返回视图
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  • 执行响应的interceptor的postHandler方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);
  • 处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

SpringMVC的九大内置组件:

doDispatch方法的执行流程:

相关推荐
Michael_lcf6 分钟前
Java的UDP通信:DatagramSocket和DatagramPacket
java·开发语言·udp
摇滚侠15 分钟前
Spring Boot 3零基础教程,WEB 开发 HttpMessageConverter @ResponseBody 注解实现内容协商源码分析 笔记33
java·spring boot·笔记
计算机毕业设计小帅32 分钟前
【2026计算机毕业设计】基于Springboot的校园电动车短租平台
spring boot·后端·课程设计
调试人生的显微镜32 分钟前
Web前端开发工具实战指南 从开发到调试的完整提效方案
后端
静心观复33 分钟前
drawio画java的uml的类图时,class和interface的区别是什么
java·uml·draw.io
Java水解33 分钟前
【SQL】MySQL中空值处理COALESCE函数
后端·mysql
Laplaces Demon34 分钟前
Spring 源码学习(十四)—— HandlerMethodArgumentResolver
java·开发语言·学习
guygg8838 分钟前
Java 无锁方式实现高性能线程
java·开发语言
ss27339 分钟前
手写Spring第7弹:Spring IoC容器深度解析:XML配置的完整指南
java·前端·数据库
Python私教41 分钟前
DRF:Django REST Framework框架介绍
后端·python·django