天天看點

SpringMVC專題(七)-SpringMVC異常處理

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

1.異常處理思路

在springmvc中,異常處理的思路

SpringMVC專題(七)-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個常見實作類:

SpringMVC專題(七)-SpringMVC異常處理

3.異常處理方案

3.1.DefaultHandlerExceptionResolver

Spring MVC預設裝配了DefaultHandlerExceptionResolver,它會将Spring MVC架構的異常轉換為相應的相應狀态碼!

異常和相應狀态碼對應表

SpringMVC專題(七)-SpringMVC異常處理

在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異常解析!

SpringMVC專題(七)-SpringMVC異常處理

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;
  }
}      

繼續閱讀