最近一直沒有時間更新是因為一直在更新自己使用的框架。
之后會慢慢帶來對之前使用的spring+mvc+mybatis的優化。
會使用一些新的特性,實現一些新的功能。
我會盡量分離業務,封裝好再拿出來。
這次帶來的是全局異常捕獲。
PS:使用的是spring4.3.7版本
PPPPS:當前使用的全局異常捕獲方式已更新,不再使用當前博文描述的方式,詳細請參考:http://www.cnblogs.com/linkstar/p/8520027.html
實現的功能
首先描述實現的功能:因為在項目中,我們不可否認的會出現異常,而且這些異常並沒有進行捕獲。經常出現的bug如空指針異常等等。
在之前的項目中,如果我們沒有進行任何配置,那么容器會自動打印錯誤的信息,如果tomcat的404頁面,400頁面等等。
如果我們在web.xml中進行如下配置,就會攔截錯誤,然后跳轉到指定的錯誤頁面。
<error-page> <error-code>500</error-code> <location>/500.jsp</location> </error-page>
但是這已經落后了,現在我們通過實現spring的HandlerExceptionResolver接口來捕獲所有的異常。
如何實現
1、新建GlobalExceptionResolver如下
/** * 全局異常捕獲 * @author XXX * */ public class GlobalExceptionResolver implements HandlerExceptionResolver{ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) { //-------------------------------------------- return null; } }
2、在spring配置文件中配置剛才新建的類
<!--全局異常捕捉 --> <bean class="com.ssm.exception.GlobalExceptionResolver" />
3、根據自己的需求修改GlobalExceptionResolver的橫線部分
在你在開發的時候,請返回null,這樣這個類就如同不起作用,之前該怎么樣就怎么樣。
當開發完成之后,根據錯誤的信息來返回需要的東西了。
首先我們可以做的是打印錯誤日志,當然也是必須的。
System.err.println("訪問" + request.getRequestURI() + " 發生錯誤, 錯誤信息:" + exception.getMessage());
這里我只是用控制台舉例,你當然可以用日志框架打印。打印信息主要是包括是訪問那個地址出現了什么錯誤。
之后如果你需要返回錯誤頁面,那么就直接在ModelAndView里面寫就行了,這里就不多舉例了,ModelAndView寫過MVC的Controller應該都熟悉。
ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("error"); return modelAndView;
Follow up
以上其實就已經實現了全局捕獲異常的功能,你可以自己拋出一個不捕獲的異常測試一下是否成功。
當然還有更多對於這個類的說明。
1、注意不同類型和來源的請求。
因為在實際項目中,可能遇到各種請求類型,如正常的get,post。也可能是來自ajax的請求。
所以如果均返回同一個ModelAndView顯然可能有點不合適,對於ajax可能需要特別處理。
還有就是如果有手機端和PC在同一個項目中的情況,那么來源不同,返回的頁面也可能不同。雖然可以交給前端去做自適應處理,但是我們還是得做好考慮。
總之,要考慮到各種不同的請求,單一返回可能並不適用所有項目。
2、GlobalExceptionResolver這個類推薦放在exception包下,屬於一種自定義異常
這個配置推薦放在和web相關的spring配置下,因為和類似一個controller
3、spring是怎么做到的?handler參數又是什么?
肯定有和我一樣的好奇寶寶。用了別人好的東西就想知道怎么實現的,但是又很怕麻煩。(推薦自己仔細讀源碼,不要聽我瞎說)
首先spring官方文檔536頁說明了HandlerExceptionResolve
而官方推薦的是使用@ExceptionHandler注解去捕獲固定的異常。
然后我查了源碼,spring源碼中
/** * Resolve the exception by iterating over the list of configured exception resolvers. * The first one to return a ModelAndView instance wins. Otherwise {@code null} is returned. */ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (resolvers != null) { for (HandlerExceptionResolver handlerExceptionResolver : resolvers) { ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex); if (mav != null) { return mav; } } } return null; }
這是spring默認實現,也就是說,我們沒有重寫的話,spring是這樣執行的,從命名來瞎說就是
如果出現異常,private List<HandlerExceptionResolver> resolvers;
處理異常解析器就會非空
通過循環異常解析器處理resolvers中的異常,然后處理。
最后返回null也就是我們之前所說的不做任何錯誤頁面的那種處理。
然后處理異常打印異常信息是在抽象類里面完成的。
/** * Check whether this resolver is supposed to apply (i.e. if the supplied handler * matches any of the configured {@linkplain #setMappedHandlers handlers} or * {@linkplain #setMappedHandlerClasses handler classes}), and then delegate * to the {@link #doResolveException} template method. */ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (shouldApplyTo(request, handler)) { if (this.logger.isDebugEnabled()) { this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex); } prepareResponse(ex, response); ModelAndView result = doResolveException(request, response, handler, ex); if (result != null) { logException(ex, request); } return result; } else { return null; } }
就是打印錯誤信息,這里我們看到handler被打印了。
打印的意思是從哪一個handler解析出什么異常。
最后如果有結果依舊返回。
總之我們可以知道的是,spring的handle在處理時發現異常后,HandlerExceptionResolver的列表會被賦值,然后進行處理。
有興趣的朋友可以繼續往下追。
最后
當然這也只是我在github上面見到的一種異常的處理方式,比我之前的好用。
所以我就拿來用了。如果有需要的就拿走,如果你有更好的也歡迎在下面分享。