spring mvc 原理初探

1、簡單的springMVC的執行流程

  1. 請求訪問由容器路由到dispatcherServlet的doDispatch方法上進行分配,在這一過程當中只處理了做爲servlet部分的邏輯,並未開始執行映射
  2. 在doDispatch方法中主要作了如下幾點:
    • 校驗是否爲文件上傳模式
    • 將request保存一份異步執行備份,以支持異步執行(異步執行是基於servlet的異步請求功能的封裝)
    • (1)獲取包含handler映射器和Integerceptor多個攔截器的執行鏈,在這一步獲取的是一個能夠處理該次請求的映射器類,並非實際映射到的業務類
    • 當獲取不到映射器則進行適當的錯誤處理
    • (2)獲取adapaer適配的執行類
    • 對GET的緩存支持,經過lastModified檢測修改時間。在咱們使用@ResquestMapping註解進行映射時,映射器不支持lastModified,直接返回 -1,靜態頁面請求支持此方法
    • (3)執行前置攔截器鏈,所有成功繼續執行
    • (4)執行映射器,到這一步以前並無對具體的業務類操做,因此參數綁定的過程和映射爲modelAndView過程都在這一步才真正開始
    • 檢查是佛進行異步處理,當爲異步處理時直接返回(異步處理主要是爲了減輕請求線程的阻塞,提高併發量)
    • (5)攔截器後處理
    • (6)視圖處理器處理
    • 最後在finally塊中處理異步請求的返回,和文件上傳的資源釋放
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request); //檢查是否須要轉換成multipart處理
				multipartRequestParsed = (processedRequest != request); //調整標識用於釋放資源

				//(1)獲取對應的handler以及Interceptor攔截器鏈
				mappedHandler = getHandler(processedRequest);

                   

				if (mappedHandler == null) { //404處理
					noHandlerFound(processedRequest, response);
					return;
				}

				//(2)根據給定的handler獲取支持的adpter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())

				// Process last-modified header, if supported by the handler.
                //當請求方法爲Get時能夠根據lastModified進行頁面緩存
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

                //(3)執行攔截器鏈
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//(4)處理handler
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                //是否註冊爲異步處理
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
                
				applyDefaultViewName(processedRequest, mv);
               //(5)攔截器後處理
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			//異常處理

            //(6)交給視圖解析器進行視圖處理
			processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);
		}
	
		finally {
            //返回前,處理異步請求
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
             //關閉文件上傳資源
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

 

2、HandlerMapping的選擇

(1)、spring提供的HandlerMapping

  在這裏僅討論4.3.13版本的spring家族css

SimpleUrlHandlerMapping:以註冊鍵值對的方式映射路徑html

    在使用springmvc時,有一個靜態文件映射處理的問題,也就是爲了讓spring將咱們的html、css、js等靜態資源放行須要配置一個指定路徑,默認爲    /**   /webjar/**兩個,這兩個路徑就是存貯在SimpleUrlHandlerMapping中的,對應的則是咱們指定的路徑地址。java

 BeanNameUrlHandlerMapping:經過註冊的beanname綁定URLweb

    經過beanname綁定URL在形式上就相似於Struts2的映射方式,即一個類對應一個映射路徑,這麼作的好處呢,是能夠支持一部分方法 ,相似於lastmodified,須要實現接口,這就只能在SimpleUrlHandlerMapping和beannameUrlHandlerMapping上實現。spring

<bean name="/hello" class="com.demo.HelloController"/>
<!-- 經過註冊的類名稱映射路徑 -->

RequestMappingHandlerMapping:經過@RequestMapping註解映射的路徑瀏覽器

 經過註解映射也是咱們最經常使用的映射方式,這種映射路徑被存貯在該類中緩存

(2)、如何選擇

    在執行getHandler(processedRequest);是以這樣的順序選擇的:併發

    對註冊的全部Handler進行循環循環順序以下mvc

  • SimpleUrlHandlerMapping  匹配一次,匹配的該對象只存儲了瀏覽器圖標一個路徑 /**/ico
  • RequestMappingHandlerMapping 匹配一次,當註解的路徑能匹配到則返回該對象,並退出循環
  • beannameUrlHandlerMapping 匹配一次,成功退出循環,返回該對象
  • SimpleUrlHandlerMapping  再次匹配,此處默認存儲了靜態資源映射設置好的映射路徑,一般在咱們將配置/**路徑映射到靜態資源後都會在此匹配成功,返回ResouceHttpHandlerMapping對象(表明靜態資源處理的對象)

    經過映射的匹配順序能夠發現,全部的靜態資源都是在類映射不成功的狀況下才會進行靜態資源查找。因此若是出現靜態資源名與類的URL映射路徑重名會出現沒法訪問靜態資源的狀況。app

例如:/hello 映射到hello()方法,則/hello.html 也會匹配到這個路徑上。(spring默認忽略擴展名)

獲取匹配映射路徑的對象

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
            /*在經過對比request獲取對應的映射對象和攔截器對象,
            並封裝到executionChain 執行鏈中返回*/
			HandlerExecutionChain handler = hm.getHandler(request);//關鍵語句
			}
           //略
		return null;
	}


protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}

 

3、執行器選擇

(1)spring提供的執行器對象

1. SimpleControllerHandlerAdapter

適配實現Controller接口的類

2.RequestMappingHandlerAdapter

用於處理@RequestMapping註解的

3HttpRequestHandlerAdapter

該對象處理返回時,不會生成modelAndview對象,用於響應靜態資源

(2)選擇順序

RequestMappingHandlerAdapter

HttpRequestHandlerAdapter

SimpleControllerHandlerAdapter

相關文章
相關標籤/搜索