一、異常基礎
異常分爲兩類:
① 未到達Controller,攔截器異常、404等
② 到達Controller,系統異常空指針等500異常
二、列舉springBoot中常用的五種統一處理異常方式
-
自定義錯誤頁面
springboot默認處理異常是當出現異常的時候,會找到內置的 /error 路徑,返回錯誤信息。那麼我們重寫error.html頁面,或者建立/resource/error目錄,添加404.html,500.html靜態頁面。
目錄結構
這裏需要注意一點,名字必須一致,建目錄時名字必須爲/error,或者error.html。
缺點:攔截所有異常,不靈活 -
@ExceptionHandle 註解處理異常
在Controller類中添加攔截異常方法並使用 @ExceptionHandler 註解@RestController public class ExceptionHandler2 { @RequestMapping("/handler2/exception") public String index() { String s = null; s.length(); return "hello world!"; } /** * 用於攔截本類的java.lang.NullPointerException異常 * 該方法需要返回一個 ModelAndView:目的是可以讓我們封裝異常信息以及視圖的指定 * 參數 Exception e:會將產生異常對象注入到方法中 */ @ExceptionHandler(value = NullPointerException.class) public ModelAndView nullExceptionHanlder(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", "ExceptionHandler2: "+e.toString()); mv.setViewName("/handler/error"); return mv; } }
缺點:只對本類起作用,針對每種類型異常都要編碼,代碼冗餘,工作量大。
-
@ControllerAdvice/@RestControllerAdvice+@ExceptionHandler 註解處理異常
首先新建統一處理類,GlobalException.java@RestControllerAdvice(basePackageClasses={ExceptionHandler3.class}) public class GlobalException { private static String errorPath = "/handler/error"; /** * 用於攔截本類的java.lang.NullPointerException異常 * 該方法需要返回一個 ModelAndView:目的是可以讓我們封裝異常信息以及視圖的指定 * 參數 Exception e:會將產生異常對象注入到方法中 */ @ExceptionHandler(value = NullPointerException.class) public ModelAndView nullExceptionHanlder(Exception e) { ModelAndView mv = new ModelAndView(); mv.addObject("error", "ExceptionHandler3: "+e.toString()); mv.setViewName(errorPath); return mv; } /** * 編寫攔截其他異常的方法.... */ }
Controller.java
@RestController public class ExceptionHandler3 { @RequestMapping("/handler3/exception") public String index() { String s = null; s.length(); return "hello world!"; } }
-
@Configuration + 配置 SimpleMappingExceptionResolver 處理異常
GlobalException4Handler4.java@Configuration public class GlobalException4Handler4 { private static String errorPath = "/handler/error"; /** * 該方法必須要有返回值。返回值類型必須是: SimpleMappingExceptionResolver * @return */ @Bean public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); // 參數一:異常的類型,注意必須是異常類型的全名 參數二:視圖名稱 properties.put("java.lang.NullPointerException",errorPath); properties.put("java.lang.ArithmeticException",errorPath); // 添加其他異常類型... // 設置異常與視圖映射信息的 resolver.setExceptionMappings(properties); return resolver; } }
@RestController public class ExceptionHandler4 { @RequestMapping("/handler4/exception") public String index() { int i = 1/0; return "hello world!"; } }
缺點:沒有異常信息!沒有異常信息!!沒有異常信息!!!
-
自定義 HandlerExceptionResolver 類處理異常
@Configuration public class GlobalException4Handler5 implements HandlerExceptionResolver { private static String errorPath = "/handler/error"; /** * 實現resolveException方法,判斷異常類型進行跳轉 * @param request * @param response * @param handler * @param ex * @return */ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); //判斷不同異常類型,做不同視圖跳轉 if(ex instanceof ArithmeticException){ mv.setViewName(errorPath); } if(ex instanceof NullPointerException){ mv.setViewName(errorPath); } // 添加其他異常類型... mv.addObject("error", "Exception5: "+ex.toString()); return mv; } }
@RestController public class ExceptionHandler5 { @RequestMapping("/handler5/exception") public String index() { int i = 1/0; return "hello world!"; } }
以上是springBoot處理異常的五種方式。
推薦使用3、5兩種方式,進行統一管理。
三、開發中處理異常demo,統一返回JSON數據,統計異常信息
-
環境: springboot2.1.8、lombok
-
項目結構:
-
代碼塊
ExceptionEnum.java/** * 返回狀態枚舉類 * @author GchengLiu */ public enum ExceptionEnum { /** * 添加異常類型代碼與信息枚舉,可以多添加一些。 */ SUCCESS(0000, "請求成功"), FAIL(1000, "請求失敗"), BODY_NOT_MATCH(1002,"請求的數據格式不符!"), NOT_FOUND(1004, "未找到該資源!"), INTERNAL_SERVER_ERROR(1005, "服務器內部錯誤!"), SERVER_BUSY(1006,"服務器正忙,請稍後再試!"); private Integer code; private String msg; ExceptionEnum(Integer code, String msg) { this.code = code; this.msg = msg; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
GlobalExceptionHandler.java
@RestControllerAdvice @Slf4j public class GlobalExceptionHandler{ /** * 可以添加自定義異常攔截 */ // ------------------------------------------------------------------ /** * 404的攔截. * * @param request * @param ex * @return */ @ResponseStatus(code = HttpStatus.NOT_FOUND) @ExceptionHandler(NoHandlerFoundException.class) public Result<String> notFound(HttpServletRequest request, Exception ex) { log.error("request uri:{} ,not found exception:{}", request.getRequestURI(), ex); return ResponseUtil.noHandlerException(request.getRequestURI(),null); } /** * 攔截500錯誤 * @param req * @param ex * @return */ @ExceptionHandler public Result<String> nullExceptionHandler(HttpServletRequest req, Exception ex){ log.error("request uri:{} server exception:{}", req.getRequestURI(), ex); return ResponseUtil.serverException(req.getRequestURI()); } }
ResponseUtil.java
/** * 請求返回類 * @author GchengLiu */ public class ResponseUtil implements Serializable { public static <T> Result<T> builderResponse(int code, String msg, String uri, T data) { Result<T> res = new Result(); res.setCode(code); res.setResMsg(msg); res.setUri(uri); res.setData(data); return res; } /** * 添加常用方法 */ public static <T> Result<T> success(String msg,String uri) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), msg, uri,null); } public static <T> Result<T> success(String msg,String uri, T data) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), msg, uri, data); } public static <T> Result<T> success(String uri, T data) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), ExceptionEnum.SUCCESS.getMsg(), uri, data); } public static <T> Result<T> success(String uri) { return builderResponse(ExceptionEnum.SUCCESS.getCode(), ExceptionEnum.SUCCESS.getMsg(), uri,null); } public static <T> Result<T> failure(String uri) { return builderResponse(ExceptionEnum.FAIL.getCode(), ExceptionEnum.FAIL.getMsg(), uri, null); } public static <T> Result<T> failure(String msg,String uri) { return builderResponse(ExceptionEnum.FAIL.getCode(), msg, uri, null); } public static <T> Result<T> failure(String uri, T date) { return builderResponse(ExceptionEnum.FAIL.getCode(), ExceptionEnum.FAIL.getMsg(),uri, date); } public static <T> Result<T> illegalRequest(String uri) { return builderResponse(ExceptionEnum.BODY_NOT_MATCH.getCode(), ExceptionEnum.BODY_NOT_MATCH.getMsg(),uri, null); } public static <T> Result<T> noHandlerException(String uri,T data) { return builderResponse(ExceptionEnum.NOT_FOUND.getCode(), ExceptionEnum.NOT_FOUND.getMsg(), uri, data); } public static <T> Result<T> serverException(String uri) { return builderResponse(ExceptionEnum.INTERNAL_SERVER_ERROR.getCode(), ExceptionEnum.INTERNAL_SERVER_ERROR.getMsg(),uri, null); } public static <T> Result<T> serverBusyException(String uri) { return builderResponse(ExceptionEnum.SERVER_BUSY.getCode(), ExceptionEnum.INTERNAL_SERVER_ERROR.getMsg(), uri,null); }
Result.java
/** * 請求返回數據格式 * @param <T> * @author GchengLiu */ @Data @NoArgsConstructor public class Result<T> { private Integer code; private String resMsg; private String uri; private T data; public String toJson() { return this.data == null ? JSON.toJSONString(this) : this.toJson(SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteMapNullValue); } public String toJson(SerializerFeature... features) { return features == null ? this.toJson() : JSON.toJSONString(this, features); } }
application.yml
server: port: 8888 debug: false spring: # json 輸出格式化與時間格式化 jackson: date-format: yyyy-MM-dd HH:mm:ss SSS time-zone: GMT+8 serialization: indent-output: true # json 過濾掉返回爲null的字段,用註解也可以 default-property-inclusion: non_null mvc: static-path-pattern: /static/** # 捕獲404異常, throw-exception-if-no-handler-found: true
-
測試
404異常{ "code" : 1004, "resMsg" : "未找到該資源!", "uri" : "/1" }
500異常
{ "code" : 1005, "resMsg" : "服務器內部錯誤!", "uri" : "/index" }
好啦,統一處理異常方式就總結到這裏。有問題不要吝嗇留言啊~