源码角度看 Spring MVC 执行流程

本文详细解析了SpringMVC的执行流程,从请求映射、处理器映射器、适配器选择,到拦截器调用、目标方法执行、异常处理、视图解析及渲染,全面阐述了SpringMVC的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring MVC 执行流程

对于 Spring MVC 的流程一直很感兴趣,所以画了个上面的流程图来梳理过程,下面用文字解释

一 映射地址是否存在

图中的第 2 步。我们在请求网页的时候,不仅仅是为了请求 Servlet,有时候需要请求一些静态资源,如 jQuery,CSS,图片等。因为SpringMVC 的 DispatchServlet 会处理所有的请求,那么对应的静态资源没有 Servlet 映射地址。该怎么办,通常在配置文件加:

<mvc:default-servlet-handler/>

来交给在面对找不到的映射地址时,就交给默认 Servlet 处理器来处理,获取静态资源。如果这都还找不到的话,只能返回 404 了。

二 处理器映射器

图中第 3 步,将请求映射到对应的处理程序,以及用于预处理和后处理的拦截器列表。充当着 url 和 Controller 之间映射关系配置的角色。最后返回了 HandleExecutionChain (包括了处理器对象和处理器拦截器)给 DispatchServlet。

HandleExecutionChain 源码的属性你可以看到一些熟悉的,包括拦截器等等

....
	private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

	private final Object handler;

	@Nullable
	private HandlerInterceptor[] interceptors;

	@Nullable
	private List<HandlerInterceptor> interceptorList;
....

三 适配器

图中第 4 步。帮助 DispatcherServlet 调用映射对应请求的处理程序,而不管该处理程序实际是如何调用的。例如,调用带注释的 Controller 需要解析注释、参数封装,数据格式转换,数据验证等操作等。这是适配器模式的应用,通过扩展适配器可以对更多的类型处理器进行执行。

HandlerAdapter 的主要目的是 DispatcherServlet 不受这些细节的影响。

四 调用拦截器

第 5 步,在适配器调用实际的请求处理程序(目标方法)时,需要先调用内置或自定义拦截器的 preHandle() 方法,如果该方法的返回值为 false,那么则不会调用请求处理程序。直接返回,不在向下进行

五 执行目标方法

第 6 步,执行处理器 Handle,也就是执行我们映射地址对应的目标方法,并且返回一个 ModelAndView 对象

六 异常处理器

第 7 步,如果在执行目标方法的过程中出现了异常,就会交给异常解析器,要么打印异常信息,返回一个异常解析器生成的 ModelAndView 对象。或者如果自定义了异常解析器,也就会安装你自定义的逻辑来。不在向下执行。

其实好像这里的异常处理器不只是出现这里,还有其他地方等等。

七 postHandle 方法

第 8 步,执行内置拦截器或自定义的 postHandle() 方法。

八 视图解析器

第 9 步,视图解析器用来解析视图,并且返回一个 View。而且拦截器的 afterCompletion() 也在这里执行了。

ViewResolver 负责解析处理结果生成 View 对象,ViewResolver 首先根据逻辑视图名称解析成物理视图名称,也就是我们的页面地址,再生成 View 对象,返回。

九 渲染视图

第 10 步,把 View 对象进行渲染结果通过页面展示给用户。

其中 View 是 Spring MVC 封装的对象,它是一个接口,Spring MVC 还提供了很多视图类,例如 jspview、pdfview,jstlView、freemarkerView等等。

源码分析

对比上面的流程,我们看源码,第一站,在 DispatchServlet 的 Service 准备工作

	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// /保存请求属性的快照,以备使用
		// 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));
				}
			}
		}

		// /将框架对象放到 request,后面处理程序和视图对象可用。
		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());

		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.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

总览

在 DispatchServlet 的 doDispatch() 方法中

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 确定当前请求的处理程序。
				// 用于获取包含处理器Handler和拦截器AdapterIntercepters的处理器执行链HandlerExecutionChain
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 确定当前请求的处理器适配器。
				// 根据HandlerExecutionChain中的处理器Handler获取处理器适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				// 执行拦截器的 preHandle 方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//通过处理器适配器 HandlerApapter 来调用处理器完成对请求的处理
				// 实际调用处理程序。
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				//// 执行拦截器的 postHandle 方法
				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);
			}
			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);
				}
			}
		}
	}

如何处理请求映射

经由 HandlerMapping 对象获取 HandlerExecutionChain (包含处理器和拦截器)

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
         logger.trace(
               "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
   return null;
}

那么 hm.getHandle() 如何?

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

经由 request 获取处理器,获取处理器 Handler 后,再获取拦截器,最后组成 HandlerExecutionChain。

获取适配器

根据 Handler 获取 HandlerAdapter。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   for (HandlerAdapter ha : this.handlerAdapters) {
      if (logger.isTraceEnabled()) {
         logger.trace("Testing handler adapter [" + ha + "]");
      }
      if (ha.supports(handler)) {
         return ha;
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: Does your handler implement a supported interface like Controller?");
}

调用目标方法

使用处理器完成对请求的处理

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {

   ((Servlet) handler).service(request, response);
   return null;
}
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;

    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
	// 继续调用 service 判断请求方法,并且调用对应的 doGet() 或 doPost() 等等
    service(request, response);
}

service(HttpServletRequest req, HttpServletResponse resp)

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // 进一步执行请求的逻辑
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                // 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);

	......
    }
}

渲染视图和解析视图名

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;


		// 处理程序是否返回一个要渲染的视图?
		if (mv != null && !mv.wasCleared()) {
		// 渲染视图和解析视图
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			//打印日志
			......
		}

		if (mappedHandler != null) {
		//调用拦截器的 afterCompletion 方法
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

继续 render 方法

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 确定请求的语言环境,并将其应用于响应。也就是国际化解析
		Locale locale =
				(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			// 我们需要解析视图名。
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
			......
		}
		else {
			// 不需要查找:ModelAndView对象包含实际的视图对象。
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// 委托视图对象进行渲染。
		....
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());
			}
			//渲染视图
			view.render(mv.getModelInternal(), request, response);
		}
		......
	}

以上只是个人理解,有错包含并指出哈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值