帶你一步一步手撕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了麼?

相關文章
相關標籤/搜索