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

Servlet 与 MVC

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

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

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

固然若是你对MVC不太熟悉的话仍是乖乖往下看吧。后端

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

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

对于用户来讲其实也就这三个步骤,可是对于服务端来讲须要作不少,这里我画了一张图供你们理解。这里其实忽略了不少 Tomcat 自己已经为咱们作的,并且 Tomcat 并不只仅只有 Host,Context。服务器

用户请求

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

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

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

这时候咱们已经经过 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了么?

相关文章
相关标签/搜索