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標記的類中去找,若也找不到,則在頁面丟擲異常。