Spring-MVC開發之全局異常捕獲全面解讀(轉載)

異常,異常。我們一定要捕獲一切該死的異常,寧可錯殺一千也不能放過一個!產品上線後的異常更要命,一定要屏蔽錯誤內容,以免暴露敏感信息!在用Spring MVC開發WEB應用時捕獲全局異常的方法基本有兩種:

  • WEB.XML,就是指定error-code和page到指定地址,這也是最傳統和常見的做法
  • 用Spring的全局異常捕獲功能,這種相對可操作性更強一些,可根據自己的需要做一後善後處理,比如日誌記錄等。

SO,本文列出Spring-MVC做WEB開發時常用全局異常捕獲的幾種解決方案拋磚引玉,互相沒有依賴,每個都可單獨使用!

1、定義服務器錯誤WEB.XML整合Spring MVC

web.xml:

 1 <error-page>
 2     <error-code>404</error-code>
 3     <location>/404</location>
 4 </error-page>
 5 <error-page>
 6     <error-code>500</error-code>
 7     <location>/500</location>
 8 </error-page>
 9 
10 <!-- 未捕獲的錯誤,同樣可指定其它異常類,或自定義異常類 -->
11 <error-page>
12     <exception-type>java.lang.Exception</exception-type>
13     <location>/uncaughtException</location>
14 </error-page>

applicationContext.xml:

1 <!-- 錯誤路徑和錯誤頁面,注意指定viewResolver -->
2 <mvc:view-controller path="/404" view-name="404"/>
3 <mvc:view-controller path="/500" view-name="500"/>
4 <mvc:view-controller path="/uncaughtException" view-name="uncaughtException"/>

 2、Spring全局異常,Controller增強方式( Advising Controllers)

異常拋出:

1 @Controller
2 public class MainController {
3     @ResponseBody
4     @RequestMapping("/")
5     public String main(){
6         throw new NullPointerException("NullPointerException Test!");
7     }
8 }

異常捕獲:

 1 //[email protected]�圍
 2 //可應用到所有@RequestMapping類或方法上的@ExceptionHandler、@InitBinder、@ModelAttribute,在這裏是@ExceptionHandler
 3 @ControllerAdvice
 4 public class AControllerAdvice {
 5     @ExceptionHandler(NullPointerException.class)
 6     @ResponseStatus(HttpStatus.BAD_REQUEST)
 7     @ResponseBody
 8     public String handleIOException(NullPointerException ex) {
 9         return ClassUtils.getShortName(ex.getClass()) + ex.getMessage();
10     }
11 }

爲了防止@ResponseStatus標註的異常被Spring框架處理,可以這樣編寫全局異常處理類:

 1 @ControllerAdvice
 2 class GlobalDefaultExceptionHandler {
 3     public static final String DEFAULT_ERROR_VIEW = "error";
 4 
 5     @ExceptionHandler(value = Exception.class)
 6     public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
 7         // If the exception is annotated with @ResponseStatus rethrow it and let
 8         // the framework handle it - like the OrderNotFoundException example
 9         // at the start of this post.
10         // AnnotationUtils is a Spring Framework utility class.
11         if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
12             throw e;
13 
14         // Otherwise setup and send the user to a default error-view.
15         ModelAndView mav = new ModelAndView();
16         mav.addObject("exception", e);
17         mav.addObject("url", req.getRequestURL());
18         mav.setViewName(DEFAULT_ERROR_VIEW);
19         return mav;
20     }
21 }

3、Spirng全局異常,配置方式

異常拋出,同上!

異常捕獲:

 1 <!-- 全局異常配置 -->
 2     <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
 3         <property name="exceptionMappings">
 4             <props>
 5                 <prop key="java.lang.Exception">errors/500</prop>
 6                 <prop key="java.lang.Throwable">errors/500</prop>
 7             </props>
 8         </property>
 9         <property name="statusCodes">
10             <props>
11                 <prop key="errors/500">500</prop>
12             </props>
13         </property>
14         <!-- 設置日誌輸出級別,不定義則默認不輸出警告等錯誤日誌信息 -->
15         <property name="warnLogCategory" value="WARN"></property>
16         <!-- 默認錯誤頁面,當找不到上面mappings中指定的異常對應視圖時,使用本默認配置 -->
17         <property name="defaultErrorView" value="errors/500"></property>
18         <!-- 默認HTTP狀態碼 -->
19         <property name="defaultStatusCode" value="500"></property>
20     </bean>

對應500錯誤的view jsp頁面:

 1 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
 2 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 4 <html>
 5 <head>
 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 7 <title>500 Error</title>
 8 </head>
 9 <body>
10     <% Exception ex = (Exception)request.getAttribute("exception"); %>
11     <H2>Exception: <%= ex.getMessage()%></H2>
12     <P/>
13     <% ex.printStackTrace(new java.io.PrintWriter(out)); %>
14 </body>
15 </html>

4、Sping全局異常,自定義異常類和異常解析
自定義異常類:

 1 public class CustomException extends RuntimeException {
 2 
 3     public CustomException(){
 4         super();
 5     }
 6 
 7     public CustomException(String msg, Throwable cause){
 8         super(msg, cause);
 9         //Do something...
10     }
11 }

拋出異常:

1 @ResponseBody
2 @RequestMapping("/ce")
3 public String ce(CustomException e){
4     throw new CustomException("msg",e);
5 }

實現異常捕獲接口HandlerExceptionResolver:

 1 public class CustomHandlerExceptionResolver implements HandlerExceptionResolver{
 2 
 3     @Override
 4     public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
 5         Map<String, Object> model = new HashMap<String, Object>();
 6         model.put("e", e);
 7         //這裏可根據不同異常引起類做不同處理方式,本例做不同返回頁面。
 8         String viewName = ClassUtils.getShortName(e.getClass());
 9         return new ModelAndView(viewName, model);
10     }
11 }

新的的HandlerExceptionResolver實現類只需在配置文件中定義即可,可以配置優先級。DispatcherServlet初始化HandlerExceptionResolver的時候會自動尋找容器中實現了HandlerExceptionResolver接口的類,然後添加進來。配置Spring支持異常捕獲:

1 <bean class="cn.bg.controller.CustomHandlerExceptionResolver"/>

5、Errors and REST

使用Restful的Controller可以使用@ResponseBody處理錯誤,首先定義一個錯誤:

1 public class ErrorInfo {
2     public final String url;
3     public final String ex;
4 
5     public ErrorInfo(String url, Exception ex) {
6         this.url = url;
7         this.ex = ex.getLocalizedMessage();
8     }
9 }

通過一個@ResponseBody返回一個錯誤實例:

1 @ResponseStatus(HttpStatus.BAD_REQUEST)
2 @ExceptionHandler(MyBadDataException.class)
3 @ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) {
4     return new ErrorInfo(req.getRequestURL(), ex);
5 } 

 

發佈了25 篇原創文章 · 獲贊 16 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章