带你一步一步手撕Spring MVC源码加手绘流程图

3,605 阅读24分钟

Servlet 与 MVC

什么是Spring MVC 其实应该说 什么是 MVC ?

Model 数据,View 视图,Controller 控制器。啪!三个东西合在一起,MVC就出来了。

这么简单? 没错,其实就是这么简单。

当然如果你对MVC不太熟悉的话还是乖乖往下看吧。

其实MVC就是处理Web请求的一种框架模式,我们思考一下用户请求的流程:

  1. 输入url
  2. 发送请求
  3. 接受响应

对于用户来说其实也就这三个步骤,但是对于服务端来说需要做很多,这里我画了一张图供大家理解。这里其实忽略了很多 Tomcat 本身已经为我们做的,而且 Tomcat 并不仅仅只有 Host,Context。

用户请求

我来解释一下,用户发送请求的 url 其实对应着很多东西。

比如说 localhost ,当然这个就是 ip 地址。这个 ip 地址对应着 Tomcat 里面的 Host (站点) 层。

Context 代表着一个 web 应用,还记得当初写 Servlet 项目的时候有一个 webapp 文件夹(里面还有个WEB-INF,最里面是web.xml)吗?也可以理解为当初写的 servlet 项目就是一个 web 应用,而用户通过 ip 地址的端口映射去找到了这个应用。

这时候我们已经通过 ip 和端口寻找到了指定的 web 应用,我们知道一个 web 应用中存在多个 servlet ,而我们如何去寻找每个请求对应的 servlet 呢? 答案还是 url ,我们通过后面的 /news 去web.xml里面寻找已经注册到应用中的 Servlet 类。

具体我再配合图中解释一下: 找到了指定的 web 应用之后,通过请求的路径 /news 去 web.xml 中寻找是否有对应的 标签,其中这个标签的子标签 标签的值需要匹配到请求的路径,这个时候 标签的值为 /news 正好匹配到了,所以我们获取了上面的标签的值然后再寻找是否有 标签的子标签 和这个值相等,如果有则获取到底下的 对应的类 并通过这个类去解析请求

总结来说就是通过 url 从 web.xml 文件中寻找到匹配的 servlet 的类

其实这就是原生的 servlet ,那么 MVC 的影子在哪呢?

别急,你要记住的是 MVC 就是对 Servlet 的封装,想要理解 MVC 就必须理解 Servlet 和 MVC 与 Servlet 的关系

SpringMVC中的DispatcherServlet

DispatcherServlet的继承结构

有没有发现这个 DispatcherServlet 其实就是一个 Servlet。也就是说 Spring MVC中最核心的部分其实就是一个 Servlet 。

我来简单解释一下相应的部分(先简单了解一下)

  • FrameworkServlet : 是 DispatcherServlet 的一个抽象父类。其中提供了加载某个对应的 web 应用程序环境的功能,还有将 GET、POST、DELETE、PUT等方法统一交给 DispatcherServlet 处理。
  • Servlet : 一个规范,用来解决 HTTP服务器和业务代码之间的耦合问题
  • GenericServlet : 提升了 ServletConfig 的作用域,在init(servletConfig)方法中调用了init()无参方法,子类可以重写这个无参初始化方法来做一些初始化自定义工作(后面分析源码中会讲到)。
  • HttpServletBean : 可以将 Servlet 配置信息作为 Bean 的属性 自动赋值给 Servlet 的属性。
  • DispatcherServlet :整个继承链中的最后一个也是最重要的一个类,是SpringMVC 实现的核心类。MVC 通过在 web.xml 中配置 DispatcherServlet 来拦截所有请求,然后通过这个 DispatcherServlet 来进行请求的分发,并调用相应的处理器去处理请求和响应消息。

有没有想起来在 SSM 框架配置的时候在 web.xml 中的配置

  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- 把所以请求都交给DispatcherServlet处理-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!-- 拦截所有 -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

好的,现在我们知道了 springMVC 中使用了一个 DispatcherServlet 去处理所有请求,而我们知道真正处理的肯定不是 DispatcherServlet ,而是具体我们在 Controller 层中写的带有 @Controller @RequestMapping 注解的类和底下的方法。DispatcherServlet 只是一个为我们分发请求到具体处理器的一个分发Servlet

那么,这个 DispatcherServlet 具体怎么工作的呢?它是如何分发请求的呢? 且听我慢慢道来。

和 DispatcherServlet 一起工作的一些组件

首先我先将这些组件简单化,并把一些不必要的先省略为了便于理解。

其实要分发请求处理请求并相应,我们可以肯定的是 我们需要使用一个映射关系Mapping 来表示 url 和对应的 处理器,使用一个 处理器Handler 来处理对应的请求。这样,我们就出来了两个最根本的角色: HandlerMappingHandler

我们再来强调一下这两者的工作。

  • HandlerMapping : 建立请求和处理器的映射关系,即我们可以通过请求去获取对应的 handler。
  • Handler : 处理请求的处理器。

这样,我们就可以再画出一个简单的流程图了。

简单的处理流程

有没有疑惑,这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的?

如果有,那么就带着这些问题往下看。

首先,这个 handlerMapping 的集合从哪来的?甭说集合,连单个你都不知道从哪来。 那么我们就从源码中找答案吧。为了你省力,我直接告诉你,DispatcherServlet 中的 doDispatch 方法中进行了 分发的主要流程。

这里我给出了简化版的 doDispatch 方法

public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 通过request在处理器映射HandlerMapping中获取相应处理器
        Object handler = getHandler(req);
        if (handler != null) {
            ... 调用handler的处理方法
        }
    }

那么这个 getHandler(request) 方法又是什么样的呢?这里我直接放 DispatcherServlet 类的源码

@Nullable
// 这里返回的是 HandlerExecutionChain 
// 其实这是一个处理器和拦截器链的组合
// 现在你就理解为返回的是一个 handler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
	        // 遍历 handlerMapping 调用它的getHanlder获取处理器
	        // 如果不为空直接返回
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

我们继续追踪 HandlerMapping 的getHandler(request) 方法。

其实进入源码你会发现,HandlerMapping 是一个接口,故这里给出一个简单的 HandlerMapping 接口代码,如果有能力可以去看源码。

public interface HandlerMapping {

    /**
     * 获取请求对应的处理器
     * @param request 请求
     * @return 处理器
     * @throws Exception 异常
     */
    Object getHandler(HttpServletRequest request) throws Exception;

}

那么,具体的实现类又是什么呢?我们思考一下,这个mapping是一个请求和处理器的映射,它是如何存的?我们当初怎么做的?

想必,你已经有答案了,在我们使用 SSM 框架的时候我们是通过 给类和方法 配置相应的注解(@Controller,@ReqeustMapping)来建立相应的 url 和 处理器方法的映射关系的。

我们再回来看源码 在idea中 可以使用 ctrl+alt+B 来查看方法实现和类实现继承。我们查看 HandlerMapping 接口的 getHandler 方法的实现,我们会发现直接跳到了 AbstractHandlerMapping 这个抽象类的方法,我们查看该方法的源码

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 获取 handler 这其实是一个抽象方法 
        // 子类可以通过不同实现来获取handler
        // 例如通过 url 和 name的映射逻辑
        // 通过 requestMapping 中的 url 和对应方法的映射逻辑
	Object handler = getHandlerInternal(request);
	if (handler == null) {
	        // 如果获取为null 的获取默认的处理器
	        // 这里子类也可以设置自己的默认处理器
		handler = getDefaultHandler();
	}
	// 如果还是没有则返回 这时候 DispatcherServlet会返回 404
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	// 如果返回的处理器是字符串 则认为它是一个beanName
	if (handler instanceof String) {
		String handlerName = (String) handler;
		// 通过beanName从IOC容器中获取相应的处理器
		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 (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}
	// 返回处理器执行链
	return executionChain;
}

如果其他的你看不懂,你只要理解我注释区域的代码就行了。我们从上面得到最重要的信息就是:真正的handler获取是在子类实现的getHandlerInternal(request)中,那我们来看一下有哪些子类。

我们可以看到其中有 AbstractHandlerMethodMapping、AbstractUrlHandlerMapping、WelcomeHandlerMapping。

我们主要关注 AbstractHandlerMethodMapping (提供方法处理器) 和 AbstractUrlHandlerMapping(提供url对应处理器映射),这里为了不耽误时间,我们直接分析 AbstractHandlerMethodMapping ,它是注解方法的映射的一个抽象类。

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 获取请求的路径
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 通过 lookupPath 来从中获取 HandlerMethod 
        // 这个HandlerMethod 又是什么?
        // 先不用管 我们继续看lookupHandlerMethod源码
    	HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    	return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
    	this.mappingRegistry.releaseReadLock();
    }
}

// 这里的逻辑稍微有些复杂 
// 你只要知道它通过请求来匹配返回处理器方法
// 如果有多个处理器方法可以处理当前Http请求 那么返回最佳匹配的处理器
@Nullable
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()) {
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		matches.sort(comparator);
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {
			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;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

到现在逻辑慢慢变得复杂起来,我们做一个小结:在 DispatcherServlet 中我们通过遍历 handlerMapping 集合并调用它的 getHandler 方法来获取handler ,这个handler 是一个 Object (因为spring会整合其他框架的处理器,并使用这些处理器处理请求 所以这里选择Object)。而 HandlerMapping 仅仅是一个接口 为了方便 抽象类 AbstractHandlerMapping 实现了这个方法并且为子类提供了自定义获取handler的 getHandlerInternal(request) 方法。 对于我们通用方式注解来标识控制器方法和url请求路径的映射是通过 AbstractHandlerMethodMapping 来获取请求对应的 HandlerMethod 的

那么,疑问又来了,HandlerMethod是什么?

还记得刚刚上面的问题么,这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的

我们现在可以来回答一下Handler的类结构了,Handler是一个Object,为了第三方框架的处理器能够接入来处理请求,spring使用了Object,而对于注解形式来说 一个处理器是一个 HandlerMethod。这里我给出 HandlerMethod 的简单实现形式,如果有能力可以查看源码。

@Data
public class HandlerMethod {
    // bean 其实这个是标识 Controller 注解的类的对象
    private Object bean;
    // 该对象的类型
    private Class<?> beanType;
    // 该类上被标识RequestMapping注解的方法
    private Method method;
}

在 HandlerMethod 中存放了控制器类和对应的方法。为什么要存放他们?你想一下,我们用@RequestMapping注解标识的方法不就是处理方法吗,HandlerMethod 中存放他们,到时候调用处理方法只需要通过反射调用这个bean的method就行了。如果不理解可以看一下我下面写的代码。

// 这里先不用管 ModelAndView
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ModelAndView modelAndView = null;
    HandlerMethod handlerMethod = ((HandlerMethod) handler);
    // 获取HandlerMethod的method
    Method method = handlerMethod.getMethod();
    if (method != null) {
        // 通过反射调用方法并返回视图对象(这就是处理方法)
        modelAndView = (ModelAndView)method.invoke(BeanFactory.getBean(handlerMethod.getBeanType()));
    }
    return modelAndView;
}

再来看看上面的问题。这个 HandlerMapping 集合从哪来?HandlerMapping 的类结构是啥样的?Handler的类结构又是什么样的

第三个问题解决了,第二个问题上面也解决了,那么第一个问题来了。

我们从一开始就只讨论了如何在HandlerMapping中取出handler 并且调用handler的处理方法,那么我们一开始遍历的这个handlerMappings集合到底从哪儿来,或者说它是什么时候被初始化的

这个时候,我们又得回到根源。我再来放这张图,不知道你们是否还记得

你能想到什么呢?我这里假设你对servlet还是有一些了解的。

我们知道 DispatcherServlet 是一个 servlet 。一个 servlet 肯定有init()方法 (还记得我上面讲的GenericServlet的作用吗?现在来了,如果不是很懂init(),建议去了解一下servlet的生命周期)。

我们可以大胆的猜测,对 handlerMappings 的初始化就是在 servlet 的初始化方法中进行的。

很遗憾我们没有能在 DispatcherServlet 中找到 init 方法,那么就找他爹,找不到再找他爷爷,曾爷爷。我们知道因为 DispatcherServlet 继承了 GenericServlet 所以我们需要找到 实现的 init() 无参方法。所以我们找到了 HttpServletBean 中重写的 init() 方法了

@Override
// 这家伙还不允许被重写 final
public final void init() throws ServletException {

	// Set bean properties from init parameters.
	// 将servlet配置信息存入bean的属性中
	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
	if (!pvs.isEmpty()) {
		try {
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
			initBeanWrapper(bw);
			bw.setPropertyValues(pvs, true);
		}
		catch (BeansException ex) {
			if (logger.isErrorEnabled()) {
				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			}
			throw ex;
		}
	}
	// 重点在这里 这里子类才可以自由发挥
	// 该方法不能被重写是因为 上面的步骤是必须的
	// 别忘了上面的步骤是 HttpServletBean 的职责
	// 接下去继续看
	// Let subclasses do whatever initialization they like.
	initServletBean();
}

// 进入FrameworkServlet 查看实现的initServletBean方法
@Override
protected final void initServletBean() throws ServletException {
        // log不用管
	getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
	if (logger.isInfoEnabled()) {
		logger.info("Initializing Servlet '" + getServletName() + "'");
	}
	long startTime = System.currentTimeMillis();
	// 重点来了
	try {
	        // 初始化容器和上下文
	        // 我们要记得现在在 FrameworkServlet中执行呢
	        // 我们进入initWebApplicationContext方法
		this.webApplicationContext = initWebApplicationContext();
		// 初始化FrameworkServlet 这里没给实现 子类也没给
		// 所以不用管
		initFrameworkServlet();
	}
	// log不用管
	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");
	}
}
// 初始化容器和上下文
protected WebApplicationContext initWebApplicationContext() {
        // 查找是否有专门的根环境 先不用管
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;
	// 如果不存在专用根环境 通常我们不会走到这 先不用管
	if (this.webApplicationContext != null) {
		// A context instance was injected at construction time -> use it
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				// The context has not yet been refreshed -> provide services such as
				// setting the parent context, setting the application context id, etc
				if (cwac.getParent() == null) {
					// The context instance was injected without an explicit parent -> set
					// the root application context (if any; may be null) as the parent
					cwac.setParent(rootContext);
				}
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	// 如果为空 
	if (wac == null) {
	// 查看是否在servlet中已经注册
		// No context instance was injected at construction time -> see if one
		// has been registered in the servlet context. If one exists, it is assumed
		// that the parent context (if any) has already been set and that the
		// user has performed any initialization such as setting the context id
		wac = findWebApplicationContext();
	}
	if (wac == null) {
	// 自己创建一个
		// No context instance is defined for this servlet -> create a local one
		wac = createWebApplicationContext(rootContext);
	}
	// 判断这个环境是否支持刷新 如果不支持 下面手动刷新 
	// 如果支持则前面已经刷新了
	if (!this.refreshEventReceived) {
		// Either the context is not a ConfigurableApplicationContext with refresh
		// support or the context injected at construction time had already been
		// refreshed -> trigger initial onRefresh manually here.
		synchronized (this.onRefreshMonitor) {
		    // !!!!!!!!!!!!!!!!!!!!!重点
		    // DispacherServlet 就是在这里实现的
			onRefresh(wac);
		}
	}

	if (this.publishContext) {
		// Publish the context as a servlet context attribute.
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
	}

	return wac;
}
// DispatcherServlet 重写了该方法
@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

// 一系列的初始化工作
protected void initStrategies(ApplicationContext context) {
        // 前面一些不用管
	initMultipartResolver(context);
	// 地域
	initLocaleResolver(context);
	// 主题
	initThemeResolver(context);
	// 重点来了!!!!
	// 初始化HandlerMapping
	initHandlerMappings(context);
	// 初始化适配器
	initHandlerAdapters(context);
	// 初始化异常处理
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

我们先暂停一下,理一下思路。

HttpServletBean 中重写了 GenericServletinit() 无参方法开始初始化动作,其中HttpServletBean中先实现了 servlet 配置信息到 bean 属性信息的赋值,然后调用 initServletBean() 该方法是子类进行自定义初始化的方法。FrameworkServlet 实现了该方法并且调用了 initWebApplicationContext() 方法进行了容器和上下文的初始化工作,并且其中调用了 onRefresh(ApplicationContext context) 方法。 这里FrameworkServlet没有做任何操作而是子类 DispatcherServlet 在其中调用了 initStrategies(context) 进行初始化工作。

好了我们继续看初始化 handlerMappings方法。

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 在应用上下文中寻找 handlerMappings
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}
	
	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	// 前面不用管 其实一般我们使用默认的
	if (this.handlerMappings == null) {
	        // 这里是获取默认的handlerMappings
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
	String key = strategyInterface.getName();
	// 获取defaultStrategies的内容
	String value = defaultStrategies.getProperty(key);
	if (value != null) {
	        // 解析相应内容并初始化 handlerMappings
	        // 获取内容中的类名数组
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		List<T> strategies = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			try {
			     //通过反射创建并加入数组中取返回给上面
				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<>();
	}
}

事情马上明了了,我们现在已经知道了 handlerMapping 是怎么加入队列中了(获取到 defaultStrategies 的资源内容 遍历内容获取类名 并通过反射创建对象加入队列),所以我们可以大胆猜测 defaultStrategies 中藏着秘密,它肯定已经定义好了默认的 handlerMapping 的类名。

果不其然,我们来看代码

	private static final Properties defaultStrategies;
	// 在静态块中已经加载了defaultStrategies
	static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
		    // 通过资源初始化defaultStrategies
		    // 这里的资源路径 很重要!!!!
			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());
		}
	}
	// 在这里呀 DispatcherServlet.properties
	private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

我们去寻找一下 DispatcherServlet.properties 这个文件, 原来都给我们定义好了,我们可以看见默认的handlerMapping有两个。 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping。

好了,我们现在终于可以总结一下了。

在我定义的简单的 DispatcherServlet 的 “同事”中,主要有 HandlerMapping 和 Handler。HandlerMapping 会在 DispatcherServlet 初始化的时候被在加载 ,然后在 DispatcherServlet 调用到执行方法 doDispatch() 的时候,会遍历 handlerMappings 集合获取对应的 handler。handler 是一个 Object(因为需要适配其他框架的处理器),在注解方式中是一个 HandlerMethod (里面存了Controller类的实例和method,在处理方法的时候使用反射调用该实例的method方法)。获取完 handler之后通过 处理器的处理方法返回一个视图对象并渲染页面。

再来一个组件 Adapter

其实对于“正宗”的MVC流程中,在遍历 handlerMappings 获取到相应的 handler 之后,其实并不是直接通过 handler 来执行处理方法的,而是通过 HandlerAdapter 来执行处理方法的。

这里我写了一个简单的适配器接口,源码也不复杂 你可以直接看源码

public interface HandlerAdapter {
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    boolean support(Object handler);
}

handleRequest 不必说,用来执行处理的,里面传进去一个 handler 肯定最终调用的 handler的执行方法。这是典型的适配器模式。

support 判断该handler是否被该适配器支持。

其实理解上面的过程了之后再加入一个适配器就不难了,我们主要思考一下 为什么要加入适配器,我们知道 handler 是一个 Object 它的处理方法是不固定的,如果我们要在 DispatcherServlet 中通过 Handler 执行处理方法,那么就要做很多类型判断,这对于 DispatcherServlet 是非常难受的,所以需要通过适配器扩展。

这样我们可以写出一个简单的 doDispatch 方法了,有能力的可以查看源码

public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
    // 通过request在处理器映射HandlerMapping中获取相应处理器
    Object handler = getHandler(req);
    if (handler != null) {
        // 通过handler获取对应的适配器
        HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
        if (handlerAdapter != null) {
            // 通过适配器调用处理器的处理方法并返回ModelAndView视图对象
            ModelAndView modelAndView = handlerAdapter.handleRequest(req, resp, handler);
            ... 处理视图并渲染
        }
    }
}

视图解析

我们知道在 doDispatch 方法调用完 HandlerAdapter 的处理方法后统一返回的是一个 ModelAndView 对象,那么这个 ModelAndView 对象是什么呢?

字面意思,模型和视图。也就是 MVC 的 Model 和 View。在 SpringMVC 中 ModelAndView 是给 框架本身支持的网页生成器使用的,它是用来连接后台和网页的类,而如今在前后端分离的趋势下,基本不怎么使用了,这里我只简单提一下。

我们知道在 HandlerAdapter 调用处理方法之后会返回一个视图对象 ModelAndView ,而在这之后,doDispatch方法会调用 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException) 去处理视图信息,这个方法又会调用一个 render() 方法进行真正的视图渲染。

非常有用的RequestResponseBodyMethodProcessor

还记不记得 @RequestBody @ResponseBody @RestController 这些注解。没错,现在我们大部分都用它们,那么它们是如何工作的呢?

奥秘要从 doDispatch() 方法中的

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这条语句开始。 这个语句就是调用 相应的适配器的 handle 方法并返回 ModelAndView 对象。

当然通过前面的学习,我们知道最终调用到的是 RequestMappingHandlerAdapter 类的 handleInternal方法。

// 查看这个方法的源码 你会发现 除了处理一些 session 的问题
// 最终都会调用 处理器方法 invokeHandlerMethod
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
    	HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
    ModelAndView mav;
    checkRequest(request);
    
    // Execute invokeHandlerMethod in synchronized block if required.
    // 如果配置了 session 同步
    if (this.synchronizeOnSession) {
        // 则获取 session
    	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 {
        // 如果没有配置 session 内同步 或者还没有创建 session 直接调用处理器方法
    	// 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 中干了什么事, 看上去好密密麻麻,其实我们只要关注重点就行了,关注我注释的地方。

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	// 构造 Web 请求 其实就是一个代理类 封装了 请求和响应
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
		// 重点来了!!!!!
		// 将handlerMethod 封装成 ServletInvocableHandlerMethod类
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		// 为invocableMethod 做一些配置
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		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);
		}
		// 重点来了!!!!! 调用 ServletInvocableHandlerMethod 的 invokeAndHandle 方法
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}

		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		webRequest.requestCompleted();
	}
}

总结一下上面的方法就是:将 HandlerMethod 对象封装成 ServletInvocableHandlerMethod 然后做一些配置并调用它的 invokeAndHandle 方法

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	// 这一步很重要 执行请求并获取返回值
	// 这里里面就涉及到了 RequestBody 注解了
	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 {
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}

我们首先来解析一下 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs) 方法 ,这里涉及到了 @RequestBody 注解的使用。

@Nullable
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);
}

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	// 很简单 获取到参数
	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;
}

@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	// 获取对应的参数解析器
	HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
	if (resolver == null) {
		throw new IllegalArgumentException("Unsupported parameter type [" +
				parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
	}
	// 通过解析器解析参数 重点就在这了 因为 @RequestBody注解的存在
	// 我们会调用到 RequestResponseBodyProcessor 类的这个方法
	return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

	parameter = parameter.nestedIfOptional();
	// 主要这里面通过 MessageConverters 消息转换器 来实现了 @RequestBody 的功能
	// 由于篇幅有限 这里不再深入分析 如果想找到答案 顺着往下查看源码就行
	Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
	String name = Conventions.getVariableNameForParameter(parameter);

	if (binderFactory != null) {
		WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
		if (arg != null) {
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
				throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
			}
		}
		if (mavContainer != null) {
			mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
		}
	}

	return adaptArgumentIfNecessary(arg, parameter);
}

下面我还放了一下 readWithMessageConverters 方法的代码,其实里面主要就是遍历消息转换器,然后通过转换器执行HTTP报文到参数的转换。

for (HttpMessageConverter<?> converter : this.messageConverters) {
	Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
	GenericHttpMessageConverter<?> genericConverter =
			(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
	if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
			(targetClass != null && converter.canRead(targetClass, contentType))) {
		if (message.hasBody()) {
			HttpInputMessage msgToUse =
					getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
			body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
					((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
			body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
		}
		else {
			body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
		}
		break;
	}
}

知道了 @RequestBody 注解的原理,@ResponseBody 注解的原理也马上浮出水面了。答案就在 ServletInvocableHandlerMethod 类中的 invokeAndHandle 方法获取了 returnValue 之后的步骤

// 答案就在这里
try {
	this.returnValueHandlers.handleReturnValue(
			returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

上面两个方法你可以追踪源码,其实最终调用的还是在 RequestResponseBodyMethodProcessor 这个类中。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
		throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

	mavContainer.setRequestHandled(true);
	// 封装web请求
	ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
	ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
	// 通过消息解析器解析返回的value
	// Try even with null return value. ResponseBodyAdvice could get involved.
	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

// 这里我贴出了writeWithMessageConverters方法的主要代码 因为这个方法有点长。。
// 这里遍历 Http消息转换器集合
for (HttpMessageConverter<?> converter : this.messageConverters) {
	GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
			(GenericHttpMessageConverter<?>) converter : null);
	if (genericConverter != null ?
			((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
			converter.canWrite(valueType, selectedMediaType)) {
		body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
				(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
				inputMessage, outputMessage);
		if (body != null) {
			Object theBody = body;
			LogFormatUtils.traceDebug(logger, traceOn ->
					"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
			addContentDispositionHeader(inputMessage, outputMessage);
			// 通过转换器来输出  重点。。。
			if (genericConverter != null) {
				genericConverter.write(body, targetType, selectedMediaType, outputMessage);
			}
			else {
				((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Nothing to write: null body");
			}
		}
		return;
	}
}

我们来总结一下: 在调用 HandlerAdapter 的 处理方法的时候 会跳转调用到 RequestMappingHandlerAdapter 的 handleInternal 方法。这里面会将 原本的处理器 HandlerMethod 封装成 ServletInvocableHandlerMethod,然后会调用这个类中的 invokeAndHandle 方法,这个方法中主要进行了相应方法处理器的方法的调用,在调用之前,会将Http报文中的内容转换为对应的参数内容。在调用完成返回 returnValue 之后,会调用相应 HttpMessageConvert 的转换方法 然后返回。

最终变成什么样了呢?

现在,你理解 Spring MVC了么?