1. 程式人生 > >springMVC原始碼分析--異常處理機制HandlerExceptionResolver執行原理(二)

springMVC原始碼分析--異常處理機制HandlerExceptionResolver執行原理(二)

上一篇部落格springMVC原始碼分析--異常處理機制HandlerExceptionResolver簡單示例(一)中我們簡單地實現了一個異常處理例項,接下來我們要介紹一下HandlerExceptionResolver是如何捕獲到Controller中丟擲的異常並展示到前臺頁面的。

DispatcherServlet是springMVC中最重要的一個類,之前我們已經有部落格對其進行介紹過,可以看看其實現機制。

HandlerExceptionResolver可以捕獲Controller中丟擲的異常,因此還是和Controller的執行機制有關,我們就想到了DispatcherServlet的doDispatch方法,其異常捕獲機制還是比較簡單的,就是通過一個try catch來完成的,簡直玩的六啊!

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
			//開始捕獲Controller執行過程中丟擲的異常
			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				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;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}//捕獲Controller執行過程中丟擲的異常
			catch (Exception ex) {
				dispatchException = ex;
			}
//異常處理操作,與HandlerExceptionResolver的操作有關 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 { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }
在doDispatch方法中我們看到了如何捕獲Controller執行過程中丟擲的異常,接下來就是異常處理機制了,在processDispatchResult中,紅色區域是異常處理的操作,最終操作還是在processHandlerException中了。
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);
			}
		}

		// 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.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

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

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

processHandlerException中異常操作也是比較簡單的,springMVC啟動載入時會將所有的異常處理HandlerExceptionResolver實現類存放到handlerExceptionResolvers一個map結構中,當預設執行時,只要放回的exMv不為空就中止執行其他的異常處理實現類

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) throws Exception {

		// Check registered HandlerExceptionResolvers...
		ModelAndView exMv = null;
		for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
			exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
			if (exMv != null) {
				break;
			}
		}
		if (exMv != null) {
			if (exMv.isEmpty()) {
				request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
				return null;
			}
			// We might still need view name translation for a plain error model...
			if (!exMv.hasView()) {
				exMv.setViewName(getDefaultViewName(request));
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
			}
			WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
			return exMv;
		}

		throw ex;
	}
最終結果就放回ModelAndView變數exMV,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);
			}
		}

		// 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.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

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

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

最終獲得異常處理HandlerExceptionResolver執行後的結果ModelAndView物件,完成異常頁面的渲染工作。這個異常攔截處理的操作感覺設計的真是非常巧妙,希望能讓大家瞭解springMVC的異常處理機制。

HandlerExceptionResolver介面及實現類:


一個基於Spring MVC的Web應用程式中,可以存在多個實現了HandlerExceptionResolver的異常處理類,他們的執行順序,由其order屬性決定, order值越小,越是優先執行, 在執行到第一個返回不是null的ModelAndView的Resolver時,不再執行後續的尚未執行的Resolver的異常處理方法。