Spring-MVC核心架構介紹

一,核心流程

img

  1. 第一步:發起請求到前端控制器(DispatcherServlet)中的doDispatch方法,而後委託給doDispatch方法進行處理。
  2. 第二步:以後會請求getHandler方法,經過HandlerMapping查找 Handler(根據配置、註解進行查找)
  3. 第三步:查找到Handler以後,會把請求封裝成HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象,多個HandlerInterceptor攔截器對象)並返回。
  4. 第四步:而後經過getHandlerAdapter方法獲取支持處理這種handler的處理器適配器HandlerAdapter
  5. 第五步:獲取到的處理器適配器HandlerAdapter,將會根據適配的結果去執行Handler,一般會是由RequestMappingHandlerAdapter#handleInternal進行執行。
  6. 第六步:Handler執行完成給適配器返回ModelAndView,這裏Handler執行的一般是咱們本身編寫的controller實現。
  7. 第七步:處理器適配器向前端控制器返回ModelAndViewModelAndView是springmvc框架的一個底層對象,包括 Model和view)
  8. 第八步:前端控制器請求視圖解析器去進行視圖解析,經過這種策略很容易更換其餘視圖技術,好比thymeleaf、json等,只須要更改視圖解析器便可
  9. 第九步:視圖解析器向前端控制器返回View
  10. 第十步:前端控制器進行視圖渲染 (視圖渲染將模型數據(在ModelAndView對象中)填充到request域)
  11. 第十一步:前端控制器向用戶響應結果

二,核心組件

1,DispatcherServlet

DispatcherServlet是Spring MVC框架的中央處理器,它負責攔截全部請求,並分發到各控制器;同時提供其餘web應用開發所須要的功能。DispatcherServlet是個Servlet(它繼承自HttpServlet基類),不過能作的比這更多。它與Spring IoC容器作到了無縫集成,這意味着,Spring提供的任何特性,在Spring MVC中你均可以使用。前端

DispatcherServlet使用了特殊的bean來處理請求、渲染視圖等,這些特定的bean是框架的一部分,依賴於這些特殊的bean來進行它的初始化。Spring MVC維護了一個默認的bean列表,若是你沒有進行特別的配置,框架將會使用這些默認的bean。java

DispatcherServlet 細分以後,能夠整理出三個功能:web

  • 截獲 HTTP 請求,並交由 Spring MVC 框架處理
  • 處理調用關係
  • 初始化並裝配 Spring MVC 的各個組件

2,重要組件Bean列表

組件 Bean 類型 說明
HandlerMapping 處理器映射。它會根據某些規則將進入容器的請求映射到具體的處理器以及一系列前處理器和後處理器(即處理器攔截器)上。具體的規則視HandlerMapping類的實現不一樣而有所不一樣。其最經常使用的一個實現支持你在控制器上添加註解,配置請求路徑。固然,也存在其餘的實現。
HandlerAdapter 處理器適配器。拿到請求所對應的處理器後,適配器將負責去調用該處理器,這使得DispatcherServlet無需關心具體的調用細節。比方說,要調用的是一個基於註解配置的控制器,那麼調用前還須要從許多註解中解析出一些相應的信息。好比,調用註解實現的 Controller 須要解析其關聯的註解. HandlerAdapter的主要目的是爲了屏蔽與 DispatcherServlet 之間的諸多細節。
HandlerExceptionResolver 處理器異常解析器,它負責將捕獲的異常映射到不一樣的視圖上去,此外還支持更復雜的異常處理代碼。
ViewResolver 視圖解析器,從處理器(Handler)返回字符類型的邏輯視圖名稱解析出實際的 View 對象,該對象將渲染後的內容輸出到HTTP 響應中。
MultipartResolver 解析multi-part的傳輸請求,好比支持經過HTML表單進行的文件上傳等。

3,主要組件

3.1 處理器映射(HandlerMapping)

職責: HandlerMapping的做用是根據當前請求的找到對應的 Handler,並將 Handler(執行程序)與 HandlerInterceptor(攔截器)封裝到 HandlerExecutionChain對象(處理程序執行鏈,由處理程序對象和處理程序攔截器組成)中並返回。spring

參與時機: 在請求到達 DispatcherServlet#doDispatch的時候,會根據當前請求來肯定處理程序,詳細流程以下:json

1,首先調用getHandler方法,從容器中取出全部 HandlerMapping 接口實例並遍歷,讓 HandlerMapping 接口的實例根據本身實現類的方式去嘗試查找 Handlersession

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        //...省略
        // 查找給定請求的處理程序
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

2,在 HandlerMapping 接口的內部只有一個方法,從實現類 AbstractHandlerMapping 中的方法能夠看出,會首先根據request獲取到 handler 對象,而後經過request和 handler 來獲取 HandlerExecutionChain 處理程序執行鏈。mvc

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    //...省略
    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    //...省略
    // 獲取 處理程序攔截器 HandlerInterceptor並進行組裝,
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    return executionChain;
}

3,getHandlerExecutionChain方法首先會將以前獲取到 handler 裝入 HandlerExecutionChain 中,而後經過遍歷 處理程序攔截器 將符合條件的攔截器添加到 HandlerExecutionChain 處理程序執行鏈中並返回。app

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
			(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    //獲取攔截器
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

4,最終獲取到HandlerExecutionChain對象後,交由 HandlerAdapter (處理器適配器)進行下一步的處理。框架

3.2 處理器適配器(HandlerAdapter)以及執行流程

職責: Spring MVC的handler(ControllerHttpRequestHandler等)有多種實現方式,例如繼承Controller的,基於註解控制器方式的,HttpRequestHandler方式的。因爲實現方式不同,調用方式就不肯定了。 若是正常編寫調用,就須要使用多個if else判斷instance of,再添加實現方式,就須要修改源碼,不符合對擴展開放,對修改關閉原則,因此Spring MVC提供了處理器適配器(HandlerAdapter),屏蔽了各類handler的差別,以一種統一的方式進行處理。異步

​ 在HandlerAdapter中有三個方法,分別是supportshandlegetLastModified

public interface HandlerAdapter {
    /**
     * 判斷是否支持傳入的handler
     */
    boolean supports(Object handler);
    /**
     * 使用給定的handler處理請求
     */
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    /**
     * 返回上次修改時間,能夠返回-1表示不支持
     */
    long getLastModified(HttpServletRequest request, Object handler);
}

HandlerAdapter是一個接口,有多種實現方式,不過在咱們平時使用中,通常只會用到RequestMappingHandlerAdapter這種實現,它應該是目前springMVC主要採用的實現,針對方法級的映射匹配處理。

RequestMappingHandlerAdapter繼承自AbstractHandlerMethodAdapter抽象類,而AbstractHandlerMethodAdapterHandlerAdapter接口的抽象實現。

繼承關係:HandlerAdapter-->AbstractHandlerMethodAdapter-->RequestMappingHandlerAdapter

參與時機: 當一個請求被HandlerMapping處理結束,來處處理器適配器進行處理時:

1,首先調用getHandlerAdapter方法獲得HandlerAdapter對象,而後調用handle方法進行下一步處理。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//...省略
	// 獲取到匹配的 HandlerAdapter 對象
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	//...省略
	//進行調用
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	//...省略
}

2,而後會進入到方法AbstractHandlerMethodAdapter#handle中,進行調用handleInternal,這是一個抽象方法

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
	throws Exception {
	return handleInternal(request, response, (HandlerMethod) handler);
}

3,此時會調用實現類RequestMappingHandlerAdapter中的handleInternal方法實現,

protected ModelAndView handleInternal(HttpServletRequest request,
                                          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//...省略
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    //...省略
    return mav;
}

4,調用invokeHandlerMethod方法,它會調用咱們本身編寫Controller中的RequestMapping方法邏輯,以及其餘配置進行處理

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

	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		//設置參數處理器
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		//設置返回值理器
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		invocableMethod.setDataBinderFactory(binderFactory);
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
		// 是不是異步方法
		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

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

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

5,執行ServletInvocableHandlerMethod#invokeAndHandle方法,調用並進行處理

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
							Object... providedArgs) throws Exception {
	//1.處理調用Controller中的具體方法
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
	// 2.設置返回狀態碼
	setResponseStatus(webRequest);
	//3.當前請求無返回值或者返回值中包含錯誤,則將請求完成標識設置爲true並返回
	if (returnValue == null) {
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}
	// 4.當前請求有返回值且無錯誤信息,則將請求完成標識設置爲false,並繼續處理當前請求
	mavContainer.setRequestHandled(false);
	Assert.state(this.returnValueHandlers != null, "No return value handlers");
	try {
		this.returnValueHandlers.handleReturnValue(
			//// 選取合適的HandlerMethodReturnValueHandler,並處理返回值
			returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
	}
	catch (Exception ex) {
		if (logger.isTraceEnabled()) {
			logger.trace(formatErrorForReturnValue(returnValue), ex);
		}
		throw ex;
	}
}

其中最重要的就是第一步invokeForRequest方法:

public Object invokeForRequest(NativeWebRequest request,
                                @Nullable ModelAndViewContainer mavContainer,
                                Object... providedArgs) throws Exception {
    /**
     * 注意這裏不必定都是解析@RequestMapping方法的參數,
     * 也有可能會解析@InitBinder方法的參數
     *
     * 因此下面的doInvoke方法也並不必定調用具體的@RequestMapping方法,
     * 也有可能調用@InitBinder方法進行參數的解析綁定
     */
	// 獲取並解析請求參數
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 調用系統反射方法進行執行
    return doInvoke(args);
}

3.3 視圖解析器(ViewResolver)

職責: 在處理適配器處理完請求後,就會到視圖解析器(ViewResolver)進行下一步的處理,Spring MVC中全部控制器的處理器方法都必須返回一個邏輯視圖的名字,不管是顯式返回(好比返回一個StringView或者對象)仍是隱式返回(好比基於約定的返回)。Spring中的視圖由一個視圖名標識,並由視圖解析器來渲染。

其他待補充

4,其餘組件

4.1 攔截器(Interceptors)

職責: 能夠配置處理器攔截器HandlerInterceptors或web請求攔截器WebRequestInterceptors等攔截器,並配置它們攔截全部進入容器的請求,或限定到符合特定模式的URL路徑。這個沒什麼能夠說的,你們也都熟,咱們對Spring MVC進行功能擴展時常常會用到它。

參與時機: 在處理器映射進行Handle裝配的時候,會把攔截器給組裝成列表,而後在 處理器適配器 進行處理的時候,會按照設置對攔截器進行執行,請求到來攔截器的執行在下述REST相關組件以前,返回處理在下述REST相關組件以後。

使用方法:對SpringMVC進行擴展 中的介紹

4.2 處理器異常解析器(HandlerExceptionResolver)

職責: 對出現異常的請求進行處理,這個我們公司已經使用了統一異常攔截方式( @controlleradvice+@ExceptionHandler),對這個已經再也不使用,沒有介紹的必要。

參與時機: 當請求出現異常時。

使用方法:

4.3 具體處理請求控制器(Controller/Handler)

職責: 本身編寫的controller就是處理請求的處理器,這個不須要介紹,你們每天用。

參與時機: 在處理器適配器進行時,會匹配到咱們本身編寫的請求處理器,進行處理。

使用方法: 本身編寫,使用@Controller@xxxMapping等註解進行聲明。

4.4 文件上傳(MultipartResolver)

待補充

5,REST相關組件

5.1 處理方法參數解析器(HandlerMethodArgumentResolver)

職責: 用於 HTTP 請求中解析 HandlerMethod 參數內容,處理成Handler可用的參數數據,

參與時機: 在請求到來,進行到要對進行參數處理的時候,就會在方法參數解析器列表查找匹配的處理器,對參數進行處理。

使用方法:對SpringMVC進行擴展 中的介紹

5.2 處理方法返回值解析器(HandlerMethodReturnValueHandler)

職責: 用於 HandlerMethod 返回值解析爲 HTTP 響應內容

參與時機: 當方法處理完畢後,獲得返回值以後,就會在方法返回值解析器列表中查找匹配的處理器,針對返回值進行處理。

使用方法:對SpringMVC進行擴展 中的介紹

5.3 HTTP消息轉換器(HttpMessageConverter)

職責: HTTP 消息轉換器,用於反序列化 HTTP 請求或序列化響應。這個組件是方法級別的,

參與時機: 在請求到來時和處理完畢進行返回的時候,能夠數據進行轉換和處理,對請求數據進行處理的時機在 處理方法參數解析器 以前,對返回值進行處理時在方法返回值解析器以前。

使用方法:對SpringMVC進行擴展 中的介紹

6,部分組件運行前後順序

  1. 接收到一個請求
  2. 攔截器進行請求攔截
  3. HTTP消息轉換器進行請求處理
  4. 方法參數解析器進行處理
  5. 用戶編寫的控制器Controller進行處理
  6. 方法返回值解析器進行返回值處理(待確認)
  7. HTTP消息轉換器進行返回值處理(待確認)
  8. 攔截器進行返回值攔截處理(待確認)
  9. 返回請求結果

3、經常使用註解(瞭解便可)

請求和響應相關

註解 說明 Spring Framework 版本
@RestController 等效於 @Controller + @ResponseBody 4.0 +
@RequestMapping 應用控制器映射註解聲明 2.5+
@xxxMapping 等效於 @RequestMapping(method =RequestMethod.XXX) 4.3+
@RequestParam 獲取請求參數 2.5+
@RequestHeader 獲取請求頭 3.0+
@PathVariable 獲取請求路徑變量 3.0+
@RequestBody 獲取完整請求主體內容 3.0+
@ResponseBody 響應主體聲明 2.5+
@PostConstruct 會在依賴注入完成後被自動調用 JavaEE註解
相關文章
相關標籤/搜索