我們知道,系統中異常包括:編譯時異常和運作時異常RuntimeException,前者通過捕獲異常進而擷取異常資訊,後者主要通過規範代碼開發、測試通過手段減少運作時異常的發生。在開發中,不管是dao層、service層還是controller層,都有可能抛出異常,在springmvc中,能将所有類型的異常處理從各處理過程解耦出來,既保證了相關處理過程的功能較單一,也實作了異常資訊的統一處理和維護。
1.異常處理思路
在springmvc中,異常處理的思路
如上圖所示,系統的dao、service、controller出現異常都通過throws Exception向上抛出,最後由springmvc前端控制器交由異常處理器進行異常處理。springmvc提供全局異常處理器(一個系統隻有一個異常處理器)進行統一異常處理。明白了springmvc中的異常處理機制,下面就開始分析springmvc中的異常處理。
2.異常處理結構體系
Spring MVC通過HandlerExceptionResolver處理程式的異常,包括處理映射,資料綁定及處理器執行時發生異常。HandlerExceptionResolver僅有一個接口方法:
/**
* Interface to be implemented by objects that can resolve exceptions thrown during
* handler mapping or execution, in the typical case to error views. Implementors are
* typically registered as beans in the application context.
*
* <p>Error views are analogous to JSP error pages but can be used with any kind of
* exception including any checked exception, with potentially fine-grained mappings for
* specific handlers.
*
* @author Juergen Hoeller
* @since 22.11.2003
*/
public interface HandlerExceptionResolver {
/**
* Try to resolve the given exception that got thrown during handler execution,
* returning a {@link ModelAndView} that represents a specific error page if appropriate.
* <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding {@code ModelAndView} to forward to, or {@code null}
* for default processing
*/
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
當發生異常時,Spring MVC将調用 resolveException()方法,并轉到ModelAndView對應視圖中,作為一個異常報告頁面,回報給使用者!
HandlerExceptionResolver擁有4個常見實作類:
3.異常處理方案
3.1.DefaultHandlerExceptionResolver
Spring MVC預設裝配了DefaultHandlerExceptionResolver,它會将Spring MVC架構的異常轉換為相應的相應狀态碼!
異常和相應狀态碼對應表
在web.xml響應狀态碼配置一個對應頁面
<error-page>
<error>404</error>
<location>/static/404.html</location>
</error-page>
注意: 靜态資源注意會被DispatcherServlet攔截!
3.2.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異常解析!
3.3.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裡面。
- 不能全局控制異常。每個類都要寫一遍。
3.4.全局異常處理
上文說到 @ 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;
}
}