【SpringBoot 2.x】 開發實戰day12,springboot 統一處理異常方式

一、異常基礎

異常分爲兩類:
  ① 未到達Controller,攔截器異常、404等
  ② 到達Controller,系統異常空指針等500異常


二、列舉springBoot中常用的五種統一處理異常方式

  1. 自定義錯誤頁面
    springboot默認處理異常是當出現異常的時候,會找到內置的 /error 路徑,返回錯誤信息。那麼我們重寫error.html頁面,或者建立/resource/error目錄,添加404.html,500.html靜態頁面。
    目錄結構
       示例1
    這裏需要注意一點,名字必須一致,建目錄時名字必須爲/error,或者error.html。
    缺點:攔截所有異常,不靈活

  2. @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;
        }
    }
    

    缺點:只對本類起作用,針對每種類型異常都要編碼,代碼冗餘,工作量大。

  3. @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!";
        }
    }
    
  4. @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!";
        }
    }
    

    缺點:沒有異常信息!沒有異常信息!!沒有異常信息!!!

  5. 自定義 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數據,統計異常信息

  1. 環境: springboot2.1.8、lombok

  2. 項目結構:
       示例2

  3. 代碼塊
    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
    
  4. 測試
    404異常

    {
      "code" : 1004,
      "resMsg" : "未找到該資源!",
      "uri" : "/1"
    }
    

    500異常

    {
      "code" : 1005,
      "resMsg" : "服務器內部錯誤!",
      "uri" : "/index"
    }
    

    好啦,統一處理異常方式就總結到這裏。有問題不要吝嗇留言啊~


源碼地址

SpringBoot-Modules-Study/tree/master/springboot-Day12

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