1. 程式人生 > >SpringMVC之處理請求

SpringMVC之處理請求

接下來將會分析SpringMVC是怎麼處理請求的,首先會分析HttpServletBean、FrameServlet和DispatcherServlet,然後再看一下核心方法doDispatch
HttpServletBean主要參與建立工作,沒有涉及請求的處理
FrameworkServlet:
servlet的處理流程是:首先從Servlet介面的service方法開始,然後在HttpServlet的service方法中根據請求的型別路由到doGet、doPost等方法上:

protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//獲取請求的方法
		String method = request.getMethod();
		//根據是否是PATCH型別
		if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}

我們發現在service方法中對於除了PATCH型別,都交給了父類去處理,processRequest方法是處理請求的核心方法:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		//獲取LocaleContextHolder中原來儲存的LocaleContext
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		//獲取當前請求的LocaleContext
		LocaleContext localeContext = buildLocaleContext(request);
		//獲取原來儲存的requestAttributes 
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		//獲取當前請求的requestAttributes 
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
		
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		//設定當前請求的requestAttributes和localeContext
		initContextHolders(request, localeContext, requestAttributes);

		try {
			//請求處理的入口
			doService(request, response);
		}
		...
		finally {
			//恢復原來的設定
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			...
			//釋出一個請求處理的訊息
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

在這個方法中最終處理還是交給了doService,這個方法留給了DispatcherServlet來實現,在這之前和之後還做了LocaleContext和RequestAttributes的設定及恢復,處理完後釋出ServletRequestHandledEvent訊息以及使用request獲取到了非同步管理器並且註冊了攔截器。
DispatcherServlet
在處理請求的過程中doService是這個Servlet 的入口方法:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		Map<String, Object> attributesSnapshot = null;
		//判斷是否是include請求
		if (WebUtils.isIncludeRequest(request)) {
			//對request裡面的屬性進行快照備份
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// 設定一些屬性
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
		//用於redirect是的引數傳遞
		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
			//處理請求的核心方法
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// 如果是儲存的快照屬性不為空,需要個request恢復快照屬性
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

在這個方法中首先判斷當前request是否是include型別的,如果是需要對他裡面的屬性進行快照備份,然後個request設定上一些必要的屬性,然後呼叫doDispatch處理請求,最後如果是include型別的request,需要給它恢復屬性
doDispatch方法最核心的程式碼只有四句:

mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)

1、根據request找到對應的handler
2、根據handler找到對應的handlerAdapter
3、用handlerAdapter處理handler
4、呼叫processDispatchResult處理上面處理之後的結果
Handler:處理器,直接對應著MVC中的controller層,標註了RequestMapping的所有方法都可以看成一個Handler。只要可以實際處理請求就可以使Handler
HandlerMapping:用來查詢Handler
HandlerAdapter:讓固定的Servlet處理方法呼叫靈活的Handler來進行處理
View:展示資料
ViewResolver:查詢View
來看一下doDispatch的程式碼:

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);
				multipartRequestParsed = (processedRequest != request);

				// 根據request找到對應的Handler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 根據Handler找到對應的HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				//處理get、head請求的Last-Modified
				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;
					}
				}
				//執行攔截器的preHandle
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// HandlerAdapter使用handler處理請求
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				//如果需要非同步處理直接返回
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//如果view為空,根據request設定預設的view
				applyDefaultViewName(request, mv);
				//執行攔截器的postHandle方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			//處理上面的處理結果。包括異常處理,渲染頁面,發出完成通知觸發攔截器的afterCompletion
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			//判斷是否執行非同步請求
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// 刪除上傳請求的資源
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

doDispatch首先會檢查是不是上傳請求,如果是上傳請求,則將request轉換為MultipartHttpServletRequest,並將multipartRequestParsed標誌設定為true,然後通過getHandler方法獲取Handler處理器鏈:

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;
			}
		}
		return null;
	}

在返回的處理器鏈中,包含著一些攔截器和handler就像Pipline中的處理過程一樣
在請求處理完成之後,就要使用processDispatchResult方法處理之前返回的結果:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;
		//異常處理
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// 渲染頁面
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}
		//如果啟動了非同步處理則返回
		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			return;
		}
		//請求處理完成之後觸發攔截器AfterCompletion
		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

在處理錯誤的時候會將錯誤資訊設定到view中,然後通過render方法渲染頁面,最後觸發攔截器的攔截的事件。