1. 程式人生 > >SpringMVC的學習(六)——SpringMVC中的統一異常處理

SpringMVC的學習(六)——SpringMVC中的統一異常處理

我們知道,系統中異常包括:編譯時異常和執行時異常RuntimeException,前者通過捕獲異常從而獲取異常資訊,後者主要通過規範程式碼開發、測試通過手段減少執行時異常的發生。在開發中,不管是dao層、service層還是controller層,都有可能丟擲異常,在springmvc中,能將所有型別的異常處理從各處理過程解耦出來,既保證了相關處理過程的功能較單一,也實現了異常資訊的統一處理和維護。

一、異常處理思路

如上圖所示,系統的dao、service、controller出現異常都通過throws Exception向上丟擲,最後由springmvc前端控制器交由異常處理器進行異常處理。springmvc提供全域性異常處理器(一個系統只有一個異常處理器)進行統一異常處理。明白了springmvc中的異常處理機制,下面就開始分析springmvc中的異常處理。

二、SpringMVC異常處理

Spring MVC通過HandlerExceptionResolver處理程式的異常,包括處理對映,資料繫結及處理器執行時發生異常。HandlerExceptionResolver僅有一個介面方法:

  ModelAndView resolveException(HttpServletRequest reqeust,HttpServletResponse response,Object handler,Exception ex);

當發生異常時,Spring MVC將呼叫 resolveException()方法,並轉到ModelAndView對應檢視中,作為一個異常報告頁面,反饋給使用者!

HandlerExceptionResolver擁有4個實現類:

- DefaultHandlerExceptionResolver

- SimpleMappingExceptionResolver

- AnnotationMethodHandlerExceptionResolver

- ResponseStatusExceptionResolver

三、異常處理方案

①DefaultHandlerExceptionResolver

Spring MVC預設裝配了DefaultHandlerExceptionResolver,它會將Spring MVC框架的異常轉換為相應的相應狀態碼!

異常和相應狀態碼對應表:

在web.xml響應狀態碼配置一個對應頁面

<error-page>
	<error>404</error>
	<location>/static/404.jsp</location>
</error-page>

注意: 靜態資源注意會被DispatcherServlet攔截!

②SimpleMappingExceptionResolver

如果希望對所有的異常進行統一的處理,比如當指定的異常發生時,把它對映到要顯示的錯誤的網頁中,此時用SimpleMappingExceptionResolver進行解析。DispatcherServlet中沒有實現SimpleMappingExceptionResolver的Bean,所以需要在springmvc的配置檔案中進行配置。示例如下:

@Controller
public class DemoServlet2 {

	@RequestMapping("/testSimpleMappingExceptionResolver")
	public String testSimpleMappingExceptionResolver() {
		String[] values = new String[10];
		// 下標越界了
		System.out.println(values[11]);
		return "success";
	}
}

傳送index.jsp中的超連結請求,控制器捕獲請求後處理控制器邏輯,由於在邏輯中,陣列越界,會丟擲ArrayIndexOutOfBoundsException異常。

處理異常

<mvc:annotation-driven />  <!--註解驅動 -->
	<!-- 配置使用SimpleMappingExceptionResolver來對映異常 -->
	<bean
	class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<!-- 給異常命名一個別名 -->
		<property name="exceptionAttribute" value="ex"></property>
		<property name="exceptionMappings">
			<props>
				<!-- 一定要異常的全類名。 表示出現ArrayIndexOutOfBoundsException異常,就跳轉到error.jsp檢視 -->
				<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
			</props>
		</property>
	</bean>

另外在/WEB-INF/jsp下新建一個error.jsp檢視。因為上面配置的InternalResourceViewResolver檢視解析器預設把error字串解析為error.jsp檢視。error.jsp內容為:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>Error Page</h1>
    ${requestScope.ex}
</body>
</html>

下面重新發送index.jsp中的超連結請求後,控制器截獲請求並處理請求時,陣列越界丟擲一個ArrayIndexOutOfBoundsException一個異常,此時由SimpleMappingExceptionResolver異常解析!

③AnnotationMethodHandlerExceptionResolver

Spring MVC 預設註冊了 AnnotationMethodHandlerExceptionResolver,它允許通過@ExceptionHandler註解指定處理特定異常的方法!

@Controller
public class DemoController1 {

	@ExceptionHandler(value = { RuntimeException.class })
	public ModelAndView handleArithmeticException2(Exception ex) {
		System.out.println("[出異常了]:" + ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception", ex);
		return mv;
	}

	@ExceptionHandler(value = { ArithmeticException.class })
	public ModelAndView handleArithmeticException(Exception ex) {
		System.out.println("出異常了,算術異常:" + ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception", ex);
		return mv;
	}

	@RequestMapping("/testExceptionHandler1")
	public String test1() {
		String s=null;
		System.out.println(s.length());
		return "success";
	}
	
	@RequestMapping("/testExceptionHandler2")
	public String test2() {
		int i=100/0;
		return "success";
	}
}

目標方法內丟擲了一個ArithmeticException異常,將由繼承關係最近的異常處理捕捉到,即由handleArithmeticException捕捉到。

若將handleArithmeticException方法註釋掉,則發生ArithmeticException異常將由handleArithmeticException2進行處理。

缺點:

  • 使用該註解有一個不好的地方就是:進行異常處理的方法必須與出錯的方法在同一個Controller裡面。
  • 不能全域性控制異常。每個類都要寫一遍。

四、全域性異常處理

上文說到 @ ExceptionHandler 需要進行異常處理的方法必須與出錯的方法在同一個Controller裡面。那麼當代碼加入了 @ControllerAdvice,則不需要必須在同一個 controller 中了。這也是 Spring 3.2 帶來的新特性。從名字上可以看出大體意思是控制器增強。 也就是說,@controlleradvice + @ ExceptionHandler 也可以實現全域性的異常捕捉。

請確保此WebExceptionHandle 類能被掃描到並裝載進 Spring 容器中。

@Controller
@ControllerAdvice
public class WebExceptionHandle {

	@ExceptionHandler(Exception.class)
	public ModelAndView handleException(Exception ex) {
		System.out.println("全域性異常:ex = " + ex);
		ModelAndView modelAndView = new ModelAndView();
		
		modelAndView.setViewName("error");
		modelAndView.addObject("exception", ex);
		return modelAndView;
	}
}

此處可以捕捉全域性異常,但是不要忘了在spring配置的時候掃描該類!

若在其他的由@Controller標記的Handler類中的Handle方法丟擲異常,且沒有在Handler類中定義@ExceptionHandler方法,則會去由@ControllerAdvice標記的類中去找,若也找不到,則在頁面丟擲異常。