Spring的全域性異常處理
在J2EE專案的開發中,不管是對底層的資料庫操作過程,還是業務層的處理過程,還是控制層的處理過程,都不可避免會遇到各種可預知的、不可預知的異常需要處理。每個過程都單獨處理異常,系統的程式碼耦合度高,工作量大且不好統一,維護的工作量也很大。那麼,能不能將所有型別的異常處理從各處理過程解耦出來,這樣既保證了相關處理過程的功能較單一,也實現了異常資訊的統一處理和維護?答案是肯定的。下面將介紹使用Spring MVC統一處理異常的解決和實現過程
- 使用Spring MVC提供的SimpleMappingExceptionResolver
2. 實現Spring的異常處理介面HandlerExceptionResolver
3. 使用@ExceptionHandler註解實現異常處理
(一)SimpleMappingExceptionResolver
使用這種方式具有整合簡單、有良好的擴充套件性、對已有程式碼沒有入侵性等優點,但該方法僅能獲取到異常資訊,若在出現異常時,對需要獲取除異常以外的資料的情況不適用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Configuration @EnableWebMvc @ComponentScan(basePackages = {"com.balbala.mvc.web"}) publicclassWebMVCConfig extendsWebMvcConfigurerAdapter{ @Bean publicSimpleMappingExceptionResolver simpleMappingExceptionResolver() { SimpleMappingExceptionResolver b = newSimpleMappingExceptionResolver(); Properties mappings = new mappings.put("org.springframework.web.servlet.PageNotFound", "page-404"); mappings.put("org.springframework.dao.DataAccessException", "data-access"); mappings.put("org.springframework.transaction.TransactionException", "transaction-Failure"); b.setExceptionMappings(mappings); returnb; } } |
(二)HandlerExceptionResolver
相比第一種來說,HandlerExceptionResolver能準確顯示定義的異常處理頁面,達到了統一異常處理的目標
1.定義一個類實現HandlerExceptionResolver介面,這次貼一個自己以前的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
packagecom.athena.common.handler; importcom.athena.common.constants.ResponseCode; importcom.athena.common.exception.AthenaException; importcom.athena.common.http.RspMsg; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.web.servlet.HandlerExceptionResolver; importorg.springframework.web.servlet.ModelAndView; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; importjava.io.IOException; importjava.io.PrintWriter; /** * Created by sam on 15/4/14. */ publicclassGlobalHandlerExceptionResolver implementsHandlerExceptionResolver { privatestaticfinalLogger LOG = LoggerFactory.getLogger(GlobalHandlerExceptionResolver.class); /** * 在這裡處理所有得異常資訊 */ @Override publicModelAndView resolveException(HttpServletRequest req, HttpServletResponse resp, Object o, Exception ex) { ex.printStackTrace(); if(ex instanceofAthenaException) { //AthenaException為一個自定義異常 ex.printStackTrace(); printWrite(ex.toString(), resp); returnnewModelAndView(); } //RspMsg為一個自定義處理異常資訊的類 //ResponseCode為一個自定義錯誤碼的介面 RspMsg unknownException = null; if(ex instanceofNullPointerException) { unknownException = newRspMsg(ResponseCode.CODE_UNKNOWN, "業務判空異常", null); } else{ unknownException = newRspMsg(ResponseCode.CODE_UNKNOWN, ex.getMessage(), null); } printWrite(unknownException.toString(), resp); returnnewModelAndView(); } /** * 將錯誤資訊新增到response中 * * @param msg * @param response * @throws IOException */ publicstaticvoidprintWrite(String msg, HttpServletResponse response) { try{ PrintWriter pw = response.getWriter(); pw.write(msg); pw.flush(); pw.close(); } catch(Exception e) { e.printStackTrace(); } } } |
2.加入spring的配置中,這裡只貼出了相關部分
1 2 3 4 5 6 7 8 9 10 11 1 |
importcom.athena.common.handler.GlobalHandlerExceptionResolver; importorg.springframework.context.annotation.Bean; importcom.athena.common.handler.GlobalHandlerExceptionResolver; importorg.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * Created by sam on 15/4/14. */ publicclassWebSpringMvcConfig extendsWebMvcConfigurerAdapter { @Bean publicGlobalHandlerExceptionResolver globalHandlerExceptionResolver() { returnnewGlobalHandlerExceptionResolver(); } } |
(三)@ExceptionHandler
這是筆者現在專案的使用方式,這裡也僅貼出了相關部分
1.首先定義一個父類,實現一些基礎的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
packagecom.balabala.poet.base.spring; importcom.google.common.base.Throwables; importcom.raiyee.poet.base.exception.MessageException; importcom.raiyee.poet.base.utils.Ajax; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.core.annotation.AnnotationUtils; importorg.springframework.http.HttpStatus; importorg.springframework.web.bind.annotation.ResponseStatus; importorg.springframework.web.servlet.ModelAndView; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; importjava.io.IOException; importjava.io.PrintWriter; importjava.util.Date; publicclassBaseGlobalExceptionHandler { protectedstaticfinalLogger logger = null; protectedstaticfinalString DEFAULT_ERROR_MESSAGE = "系統忙,請稍後再試"; protectedModelAndView handleError(HttpServletRequest req, HttpServletResponse rsp, Exception e, String viewName, HttpStatus status) throwsException { if(AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) throwe; String errorMsg = e instanceofMessageException ? e.getMessage() : DEFAULT_ERROR_MESSAGE; String errorStack = Throwables.getStackTraceAsString(e); getLogger().error("Request: {} raised {}", req.getRequestURI(), errorStack); if(Ajax.isAjax(req)) { returnhandleAjaxError(rsp, errorMsg, status); } returnhandleViewError(req.getRequestURL().toString(), errorStack, errorMsg, viewName); } protectedModelAndView handleViewError(String url, String errorStack, String errorMessage, String viewName) { ModelAndView mav = newModelAndView(); mav.addObject("exception", errorStack); mav.addObject("url", url); mav.addObject("message", errorMessage); mav.addObject("timestamp", newDate()); mav.setViewName(viewName); returnmav; } protectedModelAndView handleAjaxError(HttpServletResponse rsp, String errorMessage, HttpStatus status) throwsIOException { rsp.setCharacterEncoding("UTF-8"); rsp.setStatus(status.value()); PrintWriter writer = rsp.getWriter(); writer.write(errorMessage); writer.flush(); returnnull; } publicLogger getLogger() { returnLoggerFactory.getLogger(BaseGlobalExceptionHandler.class); } } |
2.針對你需要捕捉的異常實現相對應的處理方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
packagecom.balabala.poet.base.spring; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.http.HttpStatus; importorg.springframework.web.bind.annotation.ControllerAdvice; importorg.springframework.web.bind.annotation.ExceptionHandler; importorg.springframework.web.bind.annotation.ResponseStatus; importorg.springframework.web.servlet.ModelAndView; importorg.springframework.web.servlet.NoHandlerFoundException; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; @ControllerAdvice publicclassGlobalExceptionHandler extendsBaseGlobalExceptionHandler { //比如404的異常就會被這個方法捕獲 @ExceptionHandler(NoHandlerFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) publicModelAndView handle404Error(HttpServletRequest req, HttpServletResponse rsp, Exception e) throwsException { returnhandleError(req, rsp, e, "error-front", HttpStatus.NOT_FOUND); } //500的異常會被這個方法捕獲 @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) publicModelAndView handleError(HttpServletRequest req, HttpServletResponse rsp, Exception e) throwsException { returnhandleError(req, rsp, e, "error-front", HttpStatus.INTERNAL_SERVER_ERROR); } //TODO 你也可以再寫一個方法來捕獲你的自定義異常 //TRY NOW!!! @Override publicLogger getLogger() { returnLoggerFactory.getLogger(GlobalExceptionHandler.class); } } |