spring 全域性異常處理

xz43發表於2015-11-01

      spring3.0開發d異常處理,如果只有jsp請求能捕獲到異常,是不足夠的, 有時候AJAX返回json資料時遇到異常。這時候預設的處理方式不能滿足了, 需要自定義的方式支援同步和ajax非同步異常處理。

       Spring3.0中對異常的處理方法一共提供了兩種:一種是使用HandlerExceptionResolver介面;一種是在Controller類內部使用@ExceptionHandler註解。使用第一種方式可以實現全域性異常控制,並且Spring已經提供了一個預設的實現類SimpleMappingExceptionResolver;使用第二種方式可以在Controller內部實現更個性化的異常處理方式,靈活性更高。一般來說,專案中只需要採用第一種方式就可以了,每個人都自己定義異常的展現方式,太過個性不統一。

 以配置檔案的方式進行異常管理

在配置dispacher-servlet.xml

配置多個異常處理的地方。


 以註解的方式配置異常管理

 為了方便閱讀, 把程式碼發全一點

[java] view plaincopy
  1. @Controller  
  2. public class ExceptionHandlerController  
  3. {  
  4.     private final Logger log = LoggerFactory.getLogger(getClass());  
  5.       
  6.     @ExceptionHandler(ManagerSecurityException.class)  
  7.     public ModelAndView handleManagerSecurityException(ManagerSecurityException e,  
  8.             HttpServletResponse response)  
  9.     throws IOException  
  10.     {  
  11.         log.info("Manager exception handler " + e.getMessage());  
  12.           
  13.         response.sendError(HttpServletResponse.SC_FORBIDDEN,  
  14.             e.getMessage());  
  15.          return new ModelAndView("viewName",new HashMap());  
  16.     }  
  17. }  

這樣的配置處理jstl請求jsp沒有問題, 如果是jquery的 $.post的方法請求, 那麼如果還是返回  error.jsp ,那在  js的success函式里則不能很好地輸出顯示。

問題又來了:  如果在 handleManagerSecurityException方法的上面打上註解 @ResponeBody 那麼這樣又只能返回 json格式串的異常處理, 你想同步請求跳到到error.jsp又不行啦!

解決辦法是這樣的:定製化異常處理


下面我們來看看CustomSimpleMappingExceptionResolver.java是如何做到支援JSP和JSON格式返回的異常錯誤的

[java] view plaincopy
  1. @Override  
  2. protected ModelAndView doResolveException(HttpServletRequest request,  
  3.         HttpServletResponse response,  
  4.         Object handler,  
  5.         Exception ex) {  
  6.   
  7.     // Expose ModelAndView for chosen error view.  
  8.     String viewName = determineViewName(ex, request);  
  9.     if (viewName != null) {//JSP格式返回  
  10.         if(!(request.getHeader("accept").indexOf("application/json")>-1 || request.getHeader("X-Requested-With").indexOf("XMLHttpRequest")>-1)){//如果不是非同步請求  
  11.             // Apply HTTP status code for error views, if specified.  
  12.             // Only apply it if we're processing a top-level request.  
  13.             Integer statusCode = determineStatusCode(request, viewName);  
  14.             if (statusCode != null) {  
  15.                 applyStatusCodeIfPossible(request, response, statusCode);  
  16.                 return getModelAndView(viewName, ex, request);  
  17.             }  
  18.         }else{//JSON格式返回  
  19.             Map model=new HashMap();  
  20.             if(this.logger.isDebugEnabled()){  
  21.                 model.put("debug", true);   
  22.             }//exception  
  23.             model.put(ConfigConstants.RESULT, ex.getMessage());  
  24.             model.put("failure", true);  
  25.             try {  
  26.                 response.getWriter().write("有異常啦!");  
  27.             } catch (IOException e) {  
  28.                 e.printStackTrace();  
  29.             }  
  30.             return new ModelAndView();  
  31.         }  
  32.         return null;  
  33.     }  
  34.     else {  
  35.         return null;  
  36.     }  
  37. }  

這裡判斷 request.getHeader("accept").indexOf("application/json") 是不是非同步請求

下面給出同步和非同步請求的Header

同步的text/html

=== MimeHeaders ===
host = localhost:8888
user-agent = Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
accept = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
accept-language = en,zh;q=0.7,en-gb;q=0.3
accept-encoding = gzip,deflate
accept-charset = ISO-8859-1,utf-8;q=0.7,*;q=0.7
keep-alive = 115
connection = keep-alive
cookie = JSESSIONID=BB7441E4B481FF64A5BCC8E6F596C330


非同步的application/json
=== MimeHeaders ===
host = localhost:8888
user-agent = Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8
accept = application/json, text/javascript, */*
accept-language = en,zh;q=0.7,en-gb;q=0.3
accept-encoding = gzip,deflate
accept-charset = ISO-8859-1,utf-8;q=0.7,*;q=0.7
keep-alive = 115
connection = keep-alive
content-type = application/x-www-form-urlencoded
x-requested-with = XMLHttpRequest
referer = 
cookie = JSESSIONID=A4B59EA87C9B83B71C0D455634746411


請注意

try {
     response.getWriter().write("有異常啦!");
    } catch (IOException e) {
     e.printStackTrace();
    }
    return new ModelAndView();

關鍵就在最後這1句   return new ModelAndView();

為什麼不寫 return null;   或者  return new ModelAndView('jsonView', model); 呢?


因為spring3的原始碼裡這樣寫著了

[java] view plaincopy
  1. protected ModelAndView processHandlerException(HttpServletRequest request,  
  2.         HttpServletResponse response,  
  3.         Object handler,  
  4.         Exception ex) throws Exception {  
  5.   
  6.     // Check registerer HandlerExceptionResolvers...  
  7.     ModelAndView exMv = null;  
  8.     for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {  
  9.         exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);  
  10.         if (exMv != null) {  
  11.             break;  
  12.         }  
  13.     }  
  14.     if (exMv != null) {  
  15.         if (exMv.isEmpty()) {  
  16.             return null;  
  17.         }  
  18.         if (logger.isDebugEnabled()) {  
  19.             logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv,  
  20.                     ex);  
  21.         }  
  22.         WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());  
  23.         return exMv;  
  24.     }  
  25.   
  26.     throw ex;  
  27. }  


注意看

  if (exMv != null) {
   if (exMv.isEmpty()) {
    return null;
   }

這句和最後一句:  throw ex;

如果你寫 respone.getWriter().write("有異常啦");   你也要  new一個空的ModelAndView,上面的程式碼表明, 如果你 return null;  它就跑出異常  throw ex;    如果沒人接收, 則會呼叫你配置的預設異常處理器 或者中斷不做任何輸出。


好了, 這樣可以同時支援同步和AJAX非同步請求異常處理了。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9399028/viewspace-1820428/,如需轉載,請註明出處,否則將追究法律責任。

相關文章