DispatcherDervlet類中重點方法講解

文采杰出發表於2024-07-06
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException mavDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = mavDefiningException.getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

這是一個處理HTTP請求分發結果的方法,通常在Spring MVC的框架內部使用。我將逐步解釋這段程式碼的功能和邏輯。

  1. 方法定義:
    processDispatchResult:方法名,表示處理分發結果。
    它接收以下引數:
    HttpServletRequest request:HTTP請求物件。
    HttpServletResponse response:HTTP響應物件。
    HandlerExecutionChain mappedHandler:一個可能包含處理器(handler)及其攔截器(interceptors)的鏈。可能是null。
    ModelAndView mv:一個包含模型和檢視的物件,用於渲染響應。也可能是null。
    Exception exception:在請求處理過程中可能丟擲的異常。可能是null。

  2. 異常處理:
    如果exception不為null,說明在請求處理過程中發生了異常。
    如果異常是ModelAndViewDefiningException的例項,則記錄一個debug日誌,並從異常中獲取ModelAndView物件。
    如果不是ModelAndViewDefiningException,則呼叫processHandlerException方法來處理異常,並嘗試獲取一個ModelAndView物件。如果該方法返回了非null的ModelAndView,則errorView被設定為true。

  3. 檢視渲染:
    如果mv不為null且沒有被清除(wasCleared()返回false),則呼叫render方法來渲染該檢視。
    如果這是一個錯誤檢視(即errorView為true),則清除與錯誤請求相關的屬性。
    如果mv為null或已被清除,則記錄一個trace日誌,表示沒有檢視需要渲染。

  4. 併發處理檢查:
    使WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()檢查是否已開始併發處理。如果是,則方法直接返回,不執行後續操作。

  5. 觸發後處理:
    如果mappedHandler不為null,則呼叫其triggerAfterCompletion方法,該方法可能被用於觸發一些在請求完成後需要執行的操作(如清理資源等)。注意,這裡傳遞的最後一個引數是null,表示沒有異常需要處理(因為異常已經在之前被處理了)。

  6. 總結
    這個方法主要用於處理HTTP請求的分發結果,包括異常處理、檢視渲染和觸發後處理等操作。它在Spring MVC的框架內部使用,幫助開發者更方便地處理HTTP請求和響應。

第二個方法render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale =
				(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

		View view;
		String viewName = mv.getViewName();
		if (viewName != null) {
			// We need to resolve the view name.
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view [" + view + "] ");
		}
		try {
			if (mv.getStatus() != null) {
				request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus());
				response.setStatus(mv.getStatus().value());
			}
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "]", ex);
			}
			throw ex;
		}
	}

這段程式碼是一個render方法的實現,它在Spring MVC中通常用於將ModelAndView物件渲染到HTTP響應中。以下是對該方法的詳細解釋:

  1. 確定和設定請求的區域設定(Locale):
    程式碼首先嚐試從localeResolver(如果存在)中解析請求的區域設定。如果不存在localeResolver,則使用請求本身提供的區域設定。
    一旦確定了區域設定,它就被設定到HTTP響應中。

  2. 解析檢視:
    從ModelAndView物件中獲取檢視名稱(viewName)。
    如果檢視名稱不為空,則呼叫resolveViewName方法來解析檢視。這個方法會根據檢視名稱、模型、區域設定和請求來查詢並返回相應的View物件。
    如果resolveViewName返回null,則丟擲一個ServletException,表示無法解析該檢視名稱。
    如果檢視名稱為空,則直接從ModelAndView物件中獲取View物件。如果View物件也為空,則同樣丟擲一個ServletException。

  3. 渲染檢視:
    在渲染之前,如果ModelAndView物件中指定了HTTP狀態碼,那麼將狀態碼設定到請求和響應中。
    呼叫View物件的render方法來渲染檢視。該方法將模型資料、HTTP請求和HTTP響應作為引數,並實際執行渲染操作。

  4. 異常處理:
    如果在渲染過程中發生異常,首先檢查日誌級別是否允許除錯輸出,如果是,則記錄異常和相關的檢視資訊。
    然後重新丟擲該異常,以便呼叫者可以處理它。

  5. 日誌記錄:
    在渲染檢視之前,如果日誌級別允許跟蹤輸出,則記錄正在渲染的檢視資訊。
    這個render方法是Spring MVC框架內部處理請求和響應的關鍵部分之一。它負責將模型資料渲染到特定的檢視中,並將結果傳送回客戶端。透過這種方法,開發人員可以將控制器邏輯與檢視邏輯分離,從而提高應用程式的可維護性和可擴充套件性。

第三個方法
@Nullable
	protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {

		if (this.viewResolvers != null) {
			for (ViewResolver viewResolver : this.viewResolvers) {
				View view = viewResolver.resolveViewName(viewName, locale);
				if (view != null) {
					return view;
				}
			}
		}
		return null;
	}

這段程式碼是一個名為 resolveViewName 的方法,它在Spring MVC中通常用於解析檢視名稱以找到對應的View物件。這個方法通常在DispatcherServlet或其他處理HTTP請求並需要渲染檢視的元件中被呼叫。以下是該方法的詳細解釋:

  1. 方法簽名:
    @Nullable:這是一個註解,表明這個方法可能返回null。這對於呼叫這個方法的程式碼來說是一個有用的提示,因為呼叫者需要知道他們可能需要檢查返回值是否為null。
    protected:這是一個訪問修飾符,表示這個方法只能在同一個包內或其子類中被訪問。
    View:這是返回型別,表示該方法將返回一個View物件或null。
    resolveViewName:這是方法名,描述了該方法的主要功能——解析檢視名稱。
    引數:
    String viewName:要解析的檢視名稱。
    @Nullable Map<String, Object> model:模型資料,可能包含要傳遞給檢視的變數。儘管在這個方法內部,這個model引數沒有被直接使用,但在某些檢視解析器實現中,它可能會被用到。
    Locale locale:當前請求的區域設定,它影響檢視的解析(例如,根據區域設定選擇適當的語言或格式)。
    HttpServletRequest request:當前的HTTP請求物件。儘管這個引數也沒有在這個方法內部被直接使用,但在某些複雜的檢視解析邏輯中,它可能會被用來獲取額外的資訊。

  2. 方法體:
    首先,檢查this.viewResolvers(一個ViewResolver的集合)是否為null。這個集合包含了應用程式中配置的所有檢視解析器。
    如果viewResolvers不為null,則遍歷這個集合中的每一個ViewResolver。
    對於每個ViewResolver,呼叫其resolveViewName方法,傳入檢視名稱和區域設定作為引數。
    如果ViewResolver的resolveViewName方法返回了一個非null的View物件,則立即返回這個View物件。這意味著一旦找到匹配的檢視,就不會再檢查剩餘的檢視解析器。
    如果遍歷完所有的檢視解析器都沒有找到匹配的檢視,則方法返回null。

  3. 總結: 這個方法透過遍歷應用程式中配置的檢視解析器來解析給定的檢視名稱。如果找到匹配的檢視,就返回該檢視;否則,返回null。這種方法允許在應用程式中配置多個檢視解析器,每個解析器都可以根據自己的規則來解析檢視名稱。

第二個方法

相關文章