天天看点

SpringMVC 执行流程解析1. ContextLoaderListener2. DispatcherServlet 初始化逻辑3. DispatcherServlet 处理流程4. 总结

SpringMVC 执行流程解析

  • 1. ContextLoaderListener
  • 2. DispatcherServlet 初始化逻辑
  • 3. DispatcherServlet 处理流程
  • 4. 总结
注:SpringMVC 版本 5.2.15
SpringMVC 执行流程解析1. ContextLoaderListener2. DispatcherServlet 初始化逻辑3. DispatcherServlet 处理流程4. 总结

上面这张图许多人都看过,本文试图从源码的角度带大家分析一下该过程。

1. ContextLoaderListener

首先我们从 ContextLoaderListener 讲起,它继承自 ServletContextListener,用于监听 Web 应用程序的启动与停止。ContextLoaderListener 中的 contextInitialized() 方法用于初始化 Web 应用程序上下文。

ContextLoaderListener # contextInitialized

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
	...
	
	/**
	 * Initialize the root web application context.
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}
	...
}
           

其中又调用了 initWebApplicationContext() 方法初始化 Web 应用程序上下文

ContextLoader # initWebApplicationContext

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
		throw new IllegalStateException(
				"Cannot initialize context because there is already a root application context present - " +
				"check whether you have multiple ContextLoader* definitions in your web.xml!");
	}

	servletContext.log("Initializing Spring root WebApplicationContext");
	Log logger = LogFactory.getLog(ContextLoader.class);
	if (logger.isInfoEnabled()) {
		logger.info("Root WebApplicationContext: initialization started");
	}
	long startTime = System.currentTimeMillis();

	try {
		if (this.context == null) {
			// 创建并保存应用程序上下文到属性中
			this.context = createWebApplicationContext(servletContext);
		}
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				if (cwac.getParent() == null) {
					ApplicationContext parent = loadParentContext(servletContext);
					cwac.setParent(parent);
				}
				// 配置并刷新当前 Web 应用程序上下文
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

		ClassLoader ccl = Thread.currentThread().getContextClassLoader();
		if (ccl == ContextLoader.class.getClassLoader()) {
			currentContext = this.context;
		}
		else if (ccl != null) {
			currentContextPerThread.put(ccl, this.context);
		}

		if (logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
		}

		return this.context;
	}
	catch (RuntimeException | Error ex) {
		logger.error("Context initialization failed", ex);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
		throw ex;
	}
}
           

该方法中调用了 createWebApplicationContext() 方法创建了 Web 应用程序上下文,并调用 configureAndRefreshWebApplicationContext() 方法配置并刷新当前 Web 应用程序上下文。

ContextLoader # configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	// 为当前 Web 应用程序上下文设置一个 id
	if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
		String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
		if (idParam != null) {
			wac.setId(idParam);
		}
		else {
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}

	// 设置 ServletContext
	wac.setServletContext(sc);
	String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
	if (configLocationParam != null) {
		wac.setConfigLocation(configLocationParam);
	}

	ConfigurableEnvironment env = wac.getEnvironment();
	if (env instanceof ConfigurableWebEnvironment) {
		((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
	}

	customizeContext(sc, wac);
	// 刷新当前应用程序上下文
	wac.refresh();
}
           

该方法的主要作用是刷新当前应用程序上下文,实际上就是调用了 AbstractApplicationContext # refresh 方法,也就是我们常讲的启动 IOC 容器,这个方法里的具体内容这里就不讲了。

总结:

ContextLoaderListener 的作用是准备好 Web 应用程序上下文,启动 IOC 容器。

2. DispatcherServlet 初始化逻辑

先看一张 DispatcherServlet 的继承关系图

SpringMVC 执行流程解析1. ContextLoaderListener2. DispatcherServlet 初始化逻辑3. DispatcherServlet 处理流程4. 总结

其中 HttpServletBean 有一个 init() 方法,该方法是一个初始化方法

HttpServletBean # init

@Override
public final void init() throws ServletException {
	// 获取 web.xml 文件中的 <init-param> 中的参数保存到 PropertyValues 中
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			// 将 HttpServletBean 对象转换为 BeanWrapper 对象
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			// 创建一个 ResourceLoader 对象
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			// 注册属性编辑器
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			// 初始化 BeanWrapper 对象
			initBeanWrapper(bw);
			// 将属性值保存到 BeanWrapper 中
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}
	}

	// Let subclasses do whatever initialization they like.
	initServletBean();
}
           

该方法中首先尝试获取 web.xml 文件中的 init-param 值,如果获取到的话,则将它保存到 BeanWrapper 中。最后调用了 initServletBean() 方法

FrameworkServlet # initServletBean

@Override
protected final void initServletBean() throws ServletException {
	getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
	if (logger.isInfoEnabled()) {
		logger.info("Initializing Servlet '" + getServletName() + "'");
	}
	long startTime = System.currentTimeMillis();

	try {
		// 初始化 WebApplicationContext
		this.webApplicationContext = initWebApplicationContext();
		// 空方法,可由子类去具体实现
		initFrameworkServlet();
	}
	catch (ServletException | RuntimeException ex) {
		logger.error("Context initialization failed", ex);
		throw ex;
	}

	if (logger.isDebugEnabled()) {
		String value = this.enableLoggingRequestDetails ?
				"shown which may lead to unsafe logging of potentially sensitive data" :
				"masked to prevent unsafe logging of potentially sensitive data";
		logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
				"': request parameters and headers will be " + value);
	}

	if (logger.isInfoEnabled()) {
		logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
	}
}
           

该方法中主要调用了 initWebApplicationContext() 去初始化 web 应用程序上下文

FrameworkServlet # initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
	WebApplicationContext rootContext =
		WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;

	if (this.webApplicationContext != null) {
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				if (cwac.getParent() == null) {
					cwac.setParent(rootContext);
				}
				// 配置并刷新 web 应用程序上下文
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {
		// 去获取 web 应用程序上下文
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		// 创建 web 应用程序上下文
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		synchronized (this.onRefreshMonitor) {
			// 刷新 web 应用程序上下文
			onRefresh(wac);
		}
	}

	if (this.publishContext) {
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}

	return wac;
}
           

该方法主要是去创建一个新的 web 应用程序上下文,然后调用 onRefresh() 方法

FrameworkServlet # onRefresh

@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}
           

DispatcherServlet # initStrategies

protected void initStrategies(ApplicationContext context) {
	// 初始化 文件上传解析器
	initMultipartResolver(context);
	// 初始化 本地化解析器
	initLocaleResolver(context);
	// 初始化 主题解析器
	initThemeResolver(context);
	// 初始化 处理器映射器
	initHandlerMappings(context);
	// 初始化 处理器映射适配器
	initHandlerAdapters(context);
	// 初始化 异常解析器
	initHandlerExceptionResolvers(context);
	// 初始化 请求获取视图名转换器
	initRequestToViewNameTranslator(context);
	// 初始化 视图解析器
	initViewResolvers(context);
	// 初始化 FlashMap 管理器
	initFlashMapManager(context);
}
           

该方法中初始化了 SpringMVC 的九大组件。我以 initHandlerMappings() 方法为例讲解一下,其他组件的获取方式和它基本相同。

DispatcherServlet # initHandlerMappings

/**
 * Initialize the HandlerMappings used by this class.
 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
 * we default to BeanNameUrlHandlerMapping.
 */
private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;
	
	// 从 ApplicationContext 中去获取 HandlerMapping
	// 获取不到的话去获取对象名为 handlerMapping 的 HandlerMapping 对象
	// 再获取不到的话则注册默认的 HandlerMapping
	if (this.detectAllHandlerMappings) {
		// 从 ApplicationContext 中获取
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			// 获取名为 handlerMapping 的 HandlerMapping 对象
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			
		}
	}

	if (this.handlerMappings == null) {
		// 注册默认的 HandlerMapping
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}
           

该方法首先从 ApplicationContext 中去获取 HandlerMapping,获取不到的话去获取对象名为 handlerMapping 的 HandlerMapping 对象,再获取不到的话则注册默认的 HandlerMapping。我们看一下这个默认的 HandlerMapping 是怎么获取的。

DispatcherServlet # getDefaultStrategies

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
	String key = strategyInterface.getName();
	// 获取逻辑在这
	String value = defaultStrategies.getProperty(key);
	if (value != null) {
		// 获取类名
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		List<T> strategies = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			try {
				// 创建 Class 对象
				Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
				Object strategy = createDefaultStrategy(context, clazz);
				strategies.add((T) strategy);
			}
			catch (ClassNotFoundException ex) {
				throw new BeanInitializationException(
						"Could not find DispatcherServlet's default strategy class [" + className +
						"] for interface [" + key + "]", ex);
			}
			catch (LinkageError err) {
				throw new BeanInitializationException(
						"Unresolvable class definition for DispatcherServlet's default strategy class [" +
						className + "] for interface [" + key + "]", err);
			}
		}
		return strategies;
	}
	else {
		return new LinkedList<>();
	}
}
           

可以看到会尝试从 defaultStrategies 中获取值。然后将获取到的值通过 Class.forName() 方法去创建对象。那么我们看一下这个 defaultStrategies 是什么。

private static final Properties defaultStrategies;

static {
	try {
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
	}
}
           

defaultStrategies 是 DispatcherServlet 中的一个属性,类型为 Properties 。在 DispatcherServlet 的静态代码块中加载了 DispatcherServlet.properties 文件到了 defaultStrategies 中。

SpringMVC 执行流程解析1. ContextLoaderListener2. DispatcherServlet 初始化逻辑3. DispatcherServlet 处理流程4. 总结

DispatcherServlet.properties

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
           

在 DispatcherServlet.properties 中定义了 HandlerMapping、HandlerAdapter、ViewResolver 等类。它们的获取逻辑是相似的。通过文件中定义的全限定名,然后调用 Class.forName() 方法去创建对象。

3. DispatcherServlet 处理流程

以 get 请求为例。当发送 get 请求时,由 FrameworkServlet # doGet 方法进行处理。

FrameworkServlet # doGet

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}
           

该方法中又调用了 processRequest() 方法

FrameworkServlet # processRequest

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

	long startTime = System.currentTimeMillis();
	Throwable failureCause = null;
	
	// 获取一个 LocaleContext 对象
	LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
	// 构建一个新的 LocaleContext 对象
	LocaleContext localeContext = buildLocaleContext(request);

	// 获取一个 RequestAttributes 对象
	RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
	// 构建一个新的 ServletRequestAttributes 对象
	ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
	asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
	
	// 初始化资源持有者
	// 将 localeContext 保存到 LocaleContextHolder 中
	// 将 requestAttributes 保存到 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 {
		resetContextHolders(request, previousLocaleContext, previousAttributes);
		if (requestAttributes != null) {
			requestAttributes.requestCompleted();
		}
		logResult(request, response, failureCause, asyncManager);
		publishRequestHandledEvent(request, response, startTime, failureCause);
	}
}
           

该方法中新建了一个 LocaleContext 对象和一个 ServletRequestAttributes 对象,并保存到了 LocaleContextHolder 与 RequestContextHolder 中。尤其是 RequestContextHolder 对象,我们可以通过它去获取 HttpServletRequest、HttpServletResponse 等对象。最后调用了 doService() 方法。

DispatcherServlet # doService

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
	logRequest(request);
	
	// 保留 request attributes 的快照
	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.
	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);
			}
		}
	}
}
           

该方法中又调用了 doDispatch() 方法

DispatcherServlet # 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);

			// 获取 HandlerExecutionChain 
			// 该方法中确定了用哪个处理器处理当前请求
			// 并添加了拦截器
			mappedHandler = getHandler(processedRequest);
			
			// 没有找到合适的处理器
			if (mappedHandler == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			// 获取 HandlerAdapter
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// 获取请求方法
			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;
				}
			}

			// 执行拦截器的前置处理
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// 处理请求并返回 ModelAndView 对象
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
			
			applyDefaultViewName(processedRequest, mv);
			// 执行拦截器的后置处理
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			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()) {
			if (mappedHandler != null) {
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		}
		else {
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}
           

接下来我们看一下 getHandler() 方法

DispatcherServlet # getHandler

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		// 遍历所有的 HandlerMapping 去获取 HandlerExecutionChain 
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}
           

该方法中遍历了所有的 HandlerMapping 去获取 Handler

AbstractHandlerMapping # getHandler

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 获取合适的 Handler
	Object handler = getHandlerInternal(request);
	if (handler == null) {
		// 没有找到合适的 Handler,则使用默认的 Handler
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	
	// 获取到了该 Handler 的字符串名,则通过字符串名去获取对应的对象
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}

	// 为当前请求添加拦截器
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}

	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		config = (config != null ? config.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}
           

该方法中去获取合适的 Handler,获取失败的话,则会使用默认的 Handler。并为该请求添加拦截器。

RequestMappingInfoHandlerMapping # getHandlerInternal

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
	try {
		return super.getHandlerInternal(request);
	}
	finally {
		ProducesRequestCondition.clearMediaTypesAttribute(request);
	}
}
           

AbstractHandlerMethodMapping # getHandlerInternal

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	// 获取请求路径
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	request.setAttribute(LOOKUP_PATH, lookupPath);
	// 加读锁
	this.mappingRegistry.acquireReadLock();
	try {
		// 根据请求路径获取对应的 HandlerMethod 
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		// 释放读锁
		this.mappingRegistry.releaseReadLock();
	}
}
           

该方法中根据请求路径去获取对应的 HandlerMethod

AbstractHandlerMethodMapping # lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	// 根据请求路径获取对应的映射
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		// 获取匹配的映射
		// 这里面会做一系列的比较
		// 如:方法名比较、参数比较、请求头比较等
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	// 匹配到合适的方法,则从中找到最匹配的方法
	if (!matches.isEmpty()) {
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	// 没有找到合适的方法,返回 null
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}
           

该方法中会去获取与请求最匹配的方法,获取不到则返回 null。

这里就已经获取完 handler 了,也就是一个 HandlerMethod 对象,比如 TestController # test 。我们往下走。

@Controller
public class TestController {
	@GetMapping("/test")
	public String test() {
		return "test";
	}
}
           

DispatcherServlet # getHandlerAdapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
		for (HandlerAdapter adapter : this.handlerAdapters) {
			if (adapter.supports(handler)) {
				return adapter;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
           

该方法就是去找一个对应的 HandlerAdapter,一般是 RequestMappingHandlerAdapter。

AbstractHandlerMethodAdapter # handle

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

	return handleInternal(request, response, (HandlerMethod) handler);
}
           

RequestMappingHandlerAdapter # handleInternal

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	checkRequest(request);

	// Execute invokeHandlerMethod in synchronized block if required.
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No HttpSession available -> no mutex necessary
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {
		// No synchronization on session demanded at all...
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}

	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}

	return mav;
}
           

该方法中调用了 invokeHandlerMethod() 方法

RequestMappingHandlerAdapter # invokeHandlerMethod

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		// 找到 @InitBinder 标注的方法
		// @InitBinder 与 WebDatabinder 一起使用,用于注册自定义的属性编辑器
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		// 找到 @ModelAttribute 标注的方法
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
	
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		// 注册参数解析器
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		// 注册返回值解析器
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		invocableMethod.setDataBinderFactory(binderFactory);
		// 注册参数名发现器
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
		
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		// 调用 @ModelAttribute 标注的方法,保证它在其他方法执行前先被执行
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
		
		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String formatted = LogFormatUtils.formatValue(result, !traceOn);
				return "Resume with async result [" + formatted + "]";
			});
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}
		// 调用该方法
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}
		// 返回 ModelAndView 对象
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}
           

该方法中会找到并注册 @InitBinder 标注的方法,找到并调用 @ModelAttribute 标注的的方法。调用 invokeAndHandleI() 方法,最终返回 ModelAndView 对象

ServletInvocableHandlerMethod # invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
	// 调用方法并获取返回值
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	setResponseStatus(webRequest);

	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			disableContentCachingIfNecessary(webRequest);
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}

	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		// 使用对应的 HandlerMethodReturnValueHandler 去处理返回值
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}
           

InvocableHandlerMethod # invokeForRequest

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
	// 获取形参值
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	// 调用方法
	return doInvoke(args);
}
           

InvocableHandlerMethod # getMethodArgumentValues

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
	// 获取 MethodParameter 数组
	MethodParameter[] parameters = getMethodParameters();
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;
	}

	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			// 解析参数
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		catch (Exception ex) {
			// Leave stack trace for later, exception may actually be resolved and handled...
			if (logger.isDebugEnabled()) {
				String exMsg = ex.getMessage();
				if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
					logger.debug(formatArgumentError(parameter, exMsg));
				}
			}
			throw ex;
		}
	}
	return args;
}
           

该方法中调用了 resolveArgument() 去解析参数

HandlerMethodArgumentResolverComposite # resolveArgument

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	// 获取 HandlerMethodArgumentResolver -> RequestParamMethodArgumentResolver
	HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
	if (resolver == null) {
		throw new IllegalArgumentException("Unsupported parameter type [" +
				parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
	}
	// 解析参数
	return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
           

AbstractNamedValueMethodArgumentResolver # resolveArgument

public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	
	NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
	MethodParameter nestedParameter = parameter.nestedIfOptional();

	Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
	if (resolvedName == null) {
		throw new IllegalArgumentException(
				"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
	}

	Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
	if (arg == null) {
		if (namedValueInfo.defaultValue != null) {
			arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
		}
		else if (namedValueInfo.required && !nestedParameter.isOptional()) {
			handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
		}
		arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
	}
	else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
		arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
	}

	if (binderFactory != null) {
		WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
		try {
			arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
		}
		catch (ConversionNotSupportedException ex) {
			throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
					namedValueInfo.name, parameter, ex.getCause());
		}
		catch (TypeMismatchException ex) {
			throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
					namedValueInfo.name, parameter, ex.getCause());
		}
	}

	handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

	return arg;
}
           

AbstractNamedValueMethodArgumentResolver # getNamedValueInfo

private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
	// 先从缓存中获取
	NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
	// 缓存中获取不到
	if (namedValueInfo == null) {
		// 获取 @RequestParam 中的 value 属性值,如果有,则将参数名设置为该值
		namedValueInfo = createNamedValueInfo(parameter);
		// 如果没有使用 @RequestParam 注解或使用了但没有设置 value 属性
		// 则使用 ASM 框架去获取字节码来获取属性名
		namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
		this.namedValueInfoCache.put(parameter, namedValueInfo);
	}
	return namedValueInfo;
}
           

RequestParamMethodArgumentResolver # createNamedValueInfo

protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
	// 根据 @RequestParam 注解去创建 NamedValueInfo 
	// 如果 @RequestParam 中的 value 属性有值,则设置参数名为该值
	RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
	return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
           

AbstractNamedValueMethodArgumentResolver # updateNamedValueInfo

private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
	String name = info.name;
	if (info.name.isEmpty()) {
		name = parameter.getParameterName();
		if (name == null) {
			throw new IllegalArgumentException(
					"Name for argument of type [" + parameter.getNestedParameterType().getName() +
					"] not specified, and parameter name information not found in class file either.");
		}
	}
	String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
	return new NamedValueInfo(name, info.required, defaultValue);
}
           

MethodParameter # getParameterName

public String getParameterName() {
	if (this.parameterIndex < 0) {
		return null;
	}
	ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
	if (discoverer != null) {
		String[] parameterNames = null;
		if (this.executable instanceof Method) {
			parameterNames = discoverer.getParameterNames((Method) this.executable);
		}
		else if (this.executable instanceof Constructor) {
			parameterNames = discoverer.getParameterNames((Constructor<?>) this.executable);
		}
		if (parameterNames != null) {
			this.parameterName = parameterNames[this.parameterIndex];
		}
		this.parameterNameDiscoverer = null;
	}
	return this.parameterName;
}
           

LocalVariableTableParameterNameDiscoverer # getParameterNames

public String[] getParameterNames(Method method) {
	Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
	return doGetParameterNames(originalMethod);
}
           

LocalVariableTableParameterNameDiscoverer # doGetParameterNames

private String[] doGetParameterNames(Executable executable) {
	Class<?> declaringClass = executable.getDeclaringClass();
	Map<Executable, String[]> map = this.parameterNamesCache.computeIfAbsent(declaringClass, this::inspectClass);
	return (map != NO_DEBUG_INFO_MAP ? map.get(executable) : null);
}
           

LocalVariableTableParameterNameDiscoverer # inspectClass

private Map<Executable, String[]> inspectClass(Class<?> clazz) {
	// 获取字节码文件
	InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));
	if (is == null) {
		if (logger.isDebugEnabled()) {
			logger.debug("Cannot find '.class' file for class [" + clazz +
					"] - unable to determine constructor/method parameter names");
		}
		return NO_DEBUG_INFO_MAP;
	}
	try {
		ClassReader classReader = new ClassReader(is);
		Map<Executable, String[]> map = new ConcurrentHashMap<>(32);
		// ASM 框架提升字节码文件
		classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), 0);
		return map;
	}
	catch (IOException ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Exception thrown while reading '.class' file for class [" + clazz +
					"] - unable to determine constructor/method parameter names", ex);
		}
	}
	catch (IllegalArgumentException ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("ASM ClassReader failed to parse class file [" + clazz +
					"], probably due to a new Java class file version that isn't supported yet " +
					"- unable to determine constructor/method parameter names", ex);
		}
	}
	finally {
		try {
			is.close();
		}
		catch (IOException ex) {
		
		}
	}
	return NO_DEBUG_INFO_MAP;
}
           

走到这一步便是通过 ASM 框架来提升字节码文件获取参数名称了。所以推荐使用 @ReuquestParam 并设置 value 属性值,这样可以直接获取到参数名,避免了 ASM 框架编辑字节码所带来的性能消耗。

DispatcherServlet # processDispatchResult

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

	boolean errorView = false;

	if (exception != null) {
		if (exception instanceof ModelAndViewDefiningException) {
			logger.debug("ModelAndViewDefiningException encountered", exception);
			mv = ((ModelAndViewDefiningException) exception).getModelAndView();
		}
		else {
			Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
			mv = processHandlerException(request, response, handler, exception);
			errorView = (mv != null);
		}
	}

	if (mv != null && !mv.wasCleared()) {
		// 渲染 ModelAndView
		render(mv, request, response);
		if (errorView) {
			WebUtils.clearErrorRequestAttributes(request);
		}
	}
	else {
		if (logger.isTraceEnabled()) {
			logger.trace("No view rendering, null ModelAndView returned.");
		}
	}

	if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
		// Concurrent handling started during a forward
		return;
	}

	if (mappedHandler != null) {
		// Exception (if any) is already handled..
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}
           

本方法中调用了 render() 方法去渲染视图

DispatcherServlet # 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) {
		// 根据 viewName 解析出 View
		view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		if (view == null) {
			throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
					"' in servlet with name '" + getServletName() + "'");
		}
	}
	else {
		// No need to lookup: the ModelAndView object contains the actual View object.
		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() + "'");
		}
	}

	// Delegate to the View object for rendering.
	if (logger.isTraceEnabled()) {
		logger.trace("Rendering view [" + view + "] ");
	}
	try {
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Error rendering view [" + view + "]", ex);
		}
		throw ex;
	}
}
           

本方法中调用了 resolveViewName() 去获取 View

DispatcherServlet # resolveViewName

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
		Locale locale, HttpServletRequest request) throws Exception {

	if (this.viewResolvers != null) {
		for (ViewResolver viewResolver : this.viewResolvers) {
			// 使用 ViewResolver 去解析出 View
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
	}
	return null;
}
           

本方法中找到合适的 ViewResolver 去解析出 View

public View resolveViewName(String viewName, Locale locale) throws Exception {
	// 是否启用了缓存功能,默认启用了
	if (!isCache()) {
		return createView(viewName, locale);
	}
	else {
		// 先尝试从缓存中取 View
		Object cacheKey = getCacheKey(viewName, locale);
		View view = this.viewAccessCache.get(cacheKey);
		if (view == null) {
			synchronized (this.viewCreationCache) {
				view = this.viewCreationCache.get(cacheKey);
				if (view == null) {
					// 缓存中没有去到,交给子类去创建 View
					view = createView(viewName, locale);
					if (view == null && this.cacheUnresolved) {
						view = UNRESOLVED_VIEW;
					}
					if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
						this.viewAccessCache.put(cacheKey, view);
						this.viewCreationCache.put(cacheKey, view);
					}
				}
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace(formatKey(cacheKey) + "served from cache");
			}
		}
		return (view != UNRESOLVED_VIEW ? view : null);
	}
}
           

本方法中判断是否开启了视图缓存功能,默认是开启了的。先尝试从缓存中取 View,取不到的话则交给子类去创建 View。

UrlBasedViewResolver # createView

@Override
protected View createView(String viewName, Locale locale) throws Exception {
	// 判断该视图解析器是否能处理这个视图
	// 如果不能的话,交给其他视图解析器去处理
	if (!canHandle(viewName, locale)) {
		return null;
	}

	// 检查前缀是否为 "redirect:"
	// 有的话则是请求重定向
	if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
		// 截取 "redirect:" 后的字符串
		String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
		// 创建重定视图
		RedirectView view = new RedirectView(redirectUrl,
				isRedirectContextRelative(), isRedirectHttp10Compatible());
		String[] hosts = getRedirectHosts();
		if (hosts != null) {
			view.setHosts(hosts);
		}
		return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
	}

	// 检查前缀是否为 "forward:"
	// 有的话则是请求转发
	if (viewName.startsWith(FORWARD_URL_PREFIX)) {
		String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
		InternalResourceView view = new InternalResourceView(forwardUrl);
		return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
	}

	// 如果没有加这两个前缀的话,则回调给父类处理
	// 其实最终的处理结果是跟请求转发一样的
	return super.createView(viewName, locale);
}
           

该方法中会获取字符串前缀。前缀为 “redirect:” 则是请求重定向,为 “forward:” 则是请求转发。如果没有这两个前缀的话,又回调到父类 AbstractCachingViewResolver # createView 方法中。

AbstractCachingViewResolver # createView

protected View createView(String viewName, Locale locale) throws Exception {
	return loadView(viewName, locale);
}
           

回调到父类 AbstractCachingViewResolver # createView 方法,并调用了 loadView() 方法

InternalResourceViewResolver # buildView

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
	// 可以看到,这里创建的 View 类型跟请求转发的相同
	InternalResourceView view = (InternalResourceView) super.buildView(viewName);
	if (this.alwaysInclude != null) {
		view.setAlwaysInclude(this.alwaysInclude);
	}
	view.setPreventDispatchLoop(true);
	return view;
}
           

本方法中调用了 buildView() 方法创建 View,可以看到,这里创建的 View 类型跟请求转发的相同

UrlBasedViewResolver # buildView

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
	Class<?> viewClass = getViewClass();
	Assert.state(viewClass != null, "No view class");

	AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
	// 拼接上前缀和后缀
	// 可以从 web.xml 文件中获取
	view.setUrl(getPrefix() + viewName + getSuffix());
	view.setAttributesMap(getAttributesMap());

	String contentType = getContentType();
	if (contentType != null) {
		view.setContentType(contentType);
	}

	String requestContextAttribute = getRequestContextAttribute();
	if (requestContextAttribute != null) {
		view.setRequestContextAttribute(requestContextAttribute);
	}

	Boolean exposePathVariables = getExposePathVariables();
	if (exposePathVariables != null) {
		view.setExposePathVariables(exposePathVariables);
	}
	Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
	if (exposeContextBeansAsAttributes != null) {
		view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
	}
	String[] exposedContextBeanNames = getExposedContextBeanNames();
	if (exposedContextBeanNames != null) {
		view.setExposedContextBeanNames(exposedContextBeanNames);
	}

	return view;
}
           

本方法中将 viewName 拼接了前缀和后缀,获取到的便是最终的完整路径。这里并将 View 获取成功了。

AbstractView # render

public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {

	if (logger.isDebugEnabled()) {
		logger.debug("View " + formatViewName() +
				", model " + (model != null ? model : Collections.emptyMap()) +
				(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
	}
	
	// 创建并合并属性到 Map 中
	// 如:保存到 ModelAndView 或 Model 中的属性
	Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
	// 准备好 HttpServletResponse 对象
	prepareResponse(request, response);
	// 解析出视图
	renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
           

该方法中主要是将我们自己保存到 ModelAndView 或 Model 中的属性转换为 Map 对象。最后调用 renderMergedOutputModel() 方法。

InternalResourceView # renderMergedOutputModel

protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

	// 将属性保存到 request 域中
	exposeModelAsRequestAttributes(model, request);

	// Expose helpers as request attributes, if any.
	exposeHelpers(request);

	// 确定请求的 url
	String dispatcherPath = prepareForRendering(request, response);

	// 获取一个请求转发器
	RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
	if (rd == null) {
		throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
				"]: Check that the corresponding file exists within your web application archive!");
	}

	// 是一个 include 请求,jsp 中经常用到,实现 jsp 页面的复用
	if (useInclude(request, response)) {
		response.setContentType(getContentType());
		if (logger.isDebugEnabled()) {
			logger.debug("Including [" + getUrl() + "]");
		}
		rd.include(request, response);
	}

	else {
		if (logger.isDebugEnabled()) {
			logger.debug("Forwarding to [" + getUrl() + "]");
		}
		// 请求转发
		rd.forward(request, response);
	}
}
           

4. 总结

SpringMVC 处理请求的大致流程就是这样了。内容有点多,建议最好还是自己 debug 走一遍代码,梳理出整个脉络才好。