【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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章