全局异常捕获

什么是异常?程序在启动或者运行时没有按照预期的执行,在执行途中发生某种未知的错误,导致程序非正常停止或者报错。

在我们的程序中,肯定会伴随着很多的异常,启动时:空对象、找不到数据库、用户名密码不对等等异常,都会在程序启动时抛出异常信息,运行时:空引用、参数不匹配等等都会在程序运行时抛出异常,启动的时候抛出异常我们可以马上修改,但是程序正在运行的突然报了一个错,如果没有对这个错误做处理,用户可能会看到一堆的代码信息,很不友好,所以今天讲解一下springboot全局异常捕获。

我们先看一个程序没有做异常处理会发生什么事情,下面是一个小小的例子:

private static List list = new ArrayList();    static {        list.add("小明");        list.add("小红");        list.add(null);    }    /**     * 测试     * @return     */    @GetMapping(value = "/test")    public String test(){        for(String s: list){            if(s.equals("小红")){                log.info("听说点赞的都发财了!!!");            }else{                log.info("没点赞的好像也发财了!!!");            }        }        return "success";    }

这是一个典型的空指针异常,真正写代码的时候是不会这么干的,这里为了展示效果才这样写的,我们请求/test接口,看看会发生什么?

我靠?这是什么玩意?这要是让用户/甲方爸爸看到,那还不得被骂死?那如何解决这个问题呢?

我们将for循环这段代码加try/catch异常捕获处理。

改造后的代码:

 /**     * 测试     * @return     */    @GetMapping(value = "/test")    public String test(){        try{            for(String s: list){                if(s.equals("小红")){                    log.info("听说点赞的都发财了!!!");                }else{                    log.info("没点赞的好像也发财了!!!");                }            }        }catch (Exception e){            return "网络繁忙,请稍后再试";        }        return "success";    }

再次访问:

发现已经不是提示代码信息了,而是提示了比较友好的网络繁忙,那这个时候,你可能就会有问题了,那我岂不是需要在每个接口请求中添加一个try/catch异常捕获?这样不仅让代码的可读性变差,还会牵扯到代码的可维护性,以后接手代码的同事心里可能是崩溃的。

java有没有一种统一的处理方式呢?让特定的错误返回特定的提示,java这么强大,怎么可能没有?下面我们来讲讲如何实现springboot的全局异常捕获。

我们先定义一个全局异常捕获类:GlobalExceptionRespone,由于Exception是大部分异常的大哥大,所以我们针对Exception做一个异常处理

package com.ymy.exceptions;import com.ymy.utils.ConstantUtil;import com.ymy.vo.Result;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;@Slf4j@RestControllerAdvicepublic class GlobalExceptionRespone {    /**     * 全局异常捕获     * @param e     * @return     */    @ExceptionHandler(value = Exception.class)    public Object errorHandler( Exception e) {        log.error("网络繁忙,请稍后再试 ->",e);        return Result.faild(ConstantUtil.ResponeCode.SYS_ERROR_CODE,ConstantUtil.ResponeCode.SYS_ERROR);    }    }

这个时候我们去掉业务代码中的try/catch:

@GetMapping(value = "/test")    public String test() {        for (String s : list) {            if (s.equals("小红")) {                log.info("听说点赞的都发财了!!!");            } else {                log.info("没点赞的好像也发财了!!!");            }        }        return "success";    }

重启,运行,结果如下:

发现了什么?程序发现了空指针,但是并没有像最开始那样抛出乱信息,而是经过我们处理过的信息,这感觉是不是不要太爽,就增加一个类,就解决了程序中需要假如try/catch的苦恼。

细心的你可能发现我在全局异常捕获类中添加了一个注解:@RestControllerAdvice,这个注解有着怎样的魅力呢?

@RestControllerAdvice是一个组合注解:@ControllerAdvice+@ResponseBody

spring4.3之后被引入的一个注解,它可以被使用到HandlerMapping中,通过@ExceptionHandler来处理HandlerMapping中的异常信息。

@ExceptionHandler通过指定特定的异常类对象来做出对应的处理,如果没有指定到特定的异常类那么他将找到与这个异常类最接近的类,比如之前的空指针异常,我们并没有针对空指针做一个全局异常处理,所以@ExceptionHandler找到了他的大哥:@Exception,如果我们配置一下空指针的异常捕获,会出现什么样的结果呢?

在全局异常捕获类中加入:

/**     * 全局异常捕获     * @param e     * @return     */    @ExceptionHandler(value = NullPointerException.class)    public Object nullPointerException( Exception e) {        log.error("程序中出现空引用,请检查! ->",e);        return Result.faild(ConstantUtil.ResponeCode.NULL_POINTER_EXCEPTION_CODE,ConstantUtil.ResponeCode.NULL_POINTER_EXCEPTION);    }

启动程序:

是不是像我刚刚说所的那样,@ExceptionHandler会优先寻找报错的异常对象,找到了直接返回,没有找到,继续往后找。

在这里我展示几种常见的异常类:

1.MissingServletRequestParameterException:缺少参数子类。

2.HttpMessageNotReadableException:参数解析失败。

3.MethodArgumentNotValidException:当对用@Valid注释的参数进行验证失败时,将引发异常。

4.BindException:参数绑定错误并且是致命错误的时候。

5.ConstraintViolationException:违反约束条件。

6.ValidationException:基础异常中所有bean验证异常的问题。

7.NoHandlerFoundException:默认情况下,当DispatcherServlet找不到请求的处理程序时,它将发送404响应。但是,如果将其属性“ throwExceptionIfNoHandlerFound” *设置为 true,则会引发此异常,并且可以使用配置的HandlerExceptionResolver进行处理。

8.HttpRequestMethodNotSupportedException:不支持的请求方式

9.HttpMediaTypeNotSupportedException:当客户端发布、放置或请求处理程序不支持的类型。

10.DuplicateKeyException:当试图插入或更新数据导致违反主键或惟一约束时引发异常。

自定义异常

需求:删除用户信息,包含三张表:用户基础信息、用户详细信息、用户图像表。

假设删除用户基础信息返回1(成功)、删除详细信息返回1(成功)、删除图像信息返回0(失败)。

如果我们将这三个放到一个事物里面,事务肯定是会提交的,因为程序没有发生异常,只是再删除的时候没有返回1而已,所以这时候我们就需要自己创建一个异常,让程序抛出。

现在我们就用一个自定义的异常来解决这个问题。

第一步:创建一个自定义的异常类继承RuntimeException(运行时异常)。

package com.ymy.exceptions;import lombok.Getter;@Getterpublic class MyException extends RuntimeException {    private static final long serialVersionUID = 1L;    /**     * 状态码     */    private String code;    /**     * 提示消息     */    private String msg;    public MyException(String code,String msg) {        this.code = code;        this.msg = msg;    }}

第二步:在全局异常类(GlobalExceptionRespone.java)中加入自定义的异常捕获:

 /**     * 业务层需要自己声明异常的情况     */    @ExceptionHandler(MyException.class)    public Object handleMyTokenExcption(MyException e) {        log.error("->",e);        return Result.faild(e.getCode(),e.getMsg());    }

第三步,再返回0的时候调用自定义的异常:

/**     * 修啊给i用户信息     *     * @return     */    @RequestMapping(value = "/updateUser", method = RequestMethod.POST)    public Result addUser() {        int count = 1;        log.info("第一步:删除基础信息。。。。。。。。。。。");        log.info("第二步:删除详细信息。。。。。。。。。。。");        log.info("第三步:删除图像信息。。。。。。。。。。。,结果返回了0,表示删除失败");        count = 0;        if(count == 0){            throw  new MyException("delete_excption","删除图像信息失败,请过一会在尝试");        }        return Result.OK();    }

这里就不具体去实现了,主要是看如何调用自定义的异常使用,调用updateUser接口:

程序抛出了异常,这就会导致事务不会提交,是我们的预期结果,我们再来看看返回给用户的信息:

提示消息正是我在删除失败之后调用自定义异常。

@Valid

相信很多人都是用过:@Valid,他是用来校验参数的一个重要工具,他能简化我们很多的if代码语句,下面我们来介绍一下他的使用方法。

需求,添加一个用户,需要添加:用户名、手机、邮箱、年龄,并且字段都不可为空,邮箱格式需要正确,年龄必须在0-120岁之间。

正常的写法是不是需要在controller中添加一堆的if语句,来判断传递的参数是否符合要求,今天我们讲解一种可读性更好,代码更简单的方式:@Valid注解。

用户VO类:

package com.ymy.vo;import lombok.Getter;import lombok.Setter;import javax.validation.constraints.*;@Getter@Setterpublic class UserVo {    @NotEmpty(message = "用户名不能为空")    private String userName;    @NotEmpty(message = "手机号不能为空")    private String phone;    @NotEmpty(message = "邮箱不可为空")    @Email(message = "邮箱格式不正确")    private String email;    @Min(0)    @Max(120)    @NotNull(message = "年龄必填")    private Integer age;}

controller接口实现:

  * 添加用户     *     * @return     */    @RequestMapping(value = "addUser", method = RequestMethod.POST)    public Result addUser(@RequestBody @Valid UserVo userVo, BindingResult bindingResult) {        if (bindingResult.hasErrors()) {            return Result.faild(ConstantUtil.ResponeCode.INVALID_PARAMETER,bindingResult.getFieldError().getDefaultMessage());        }        log.info("添加成功。。。。。。{}", userVo);        return Result.OK();    }

运行addUser接口:

发现已经实现了参数校验的功能,但是代码看着还是有一点点的不爽,那是为什么呢?因为controller中还是有着一个if的判断条件,如果能把这个if判断也去掉,那整个世界都美好了。

刚刚在讲全局异常捕获的时候提到了一个异常类,:MethodArgumentNotValidException,不知道还记得吗,他就是配合我们的@Valid使用的,具体如何使用呢?

在全局异常捕获类(GlobalExceptionRespone.java)中添加对@Valid注解参数校验失败捕获:

/**     *  校验错误拦截处理     *     * @param exception 错误信息集合     * @return 错误信息     */    @ExceptionHandler(MethodArgumentNotValidException.class)    public Object validationBodyException(MethodArgumentNotValidException exception){        BindingResult bindingResult = exception.getBindingResult();        if (bindingResult.hasErrors()) {            return Result.faild(ConstantUtil.ResponeCode.INVALID_PARAMETER,bindingResult.getFieldError().getDefaultMessage());        }        return Result.faild(ConstantUtil.ResponeCode.SYS_ERROR_CODE);    }

就是这么简单,我们现在去掉controller中的if判断:

/**     * 添加用户     *     * @return     */    @RequestMapping(value = "addUser", method = RequestMethod.POST)    public Result addUser(@RequestBody @Valid UserVo userVo) {        log.info("添加成功。。。。。。{}", userVo);        return Result.OK();    }

运行结果如下:

完美,从一堆的if判断语句到现在的一个注解,代码的可读性以及可维护性都得到了很大的提升,现在的心情就是倍爽

@Valid常用校验注解:

@Null:只能为空

@NotNull:不能为空

@Max(value):最大数字

@Min(value):最小数字

@Size(max,min):字符长度必须在min到max之间

@DecimalMax(value):不大于指定数字

@DecimalMin(value):不小于指定数字

@Pattern(value):正则表达式

@Digits(integer,fraction):小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction

@Past:日期类型:输入时间比当前时间早

@Future:将来日期

@Past:过去日期

@Email:验证邮箱格式

@AssertFalse:只能为false

@AssertTrue:只能为true

@NotEmpty:参数不能为空

@NotBlank:参数不能为空

依赖类:

package com.ymy.vo;import com.ymy.utils.ConstantUtil;import com.ymy.utils.DateUtil;import lombok.Getter;@Getterpublic class Result {    private  String  code;    private String msg;    private T data;    private String  createTime = DateUtil.getdateNow();    private  Result(String code){        this.code = code;    }    private  Result(String code,T data){        this.code = code;        this.data = data;    }    private  Result(String code,String msg){        this.code = code;        this.msg = msg;    }    private  Result(String code,String msg,T data){        this.code = code;        this.msg = msg;        this.data = data;    }    /**     * 默认成功返回     * @param      * @return     */    public static Result OK(){        return new Result(ConstantUtil.ResponeCode.SUCCESS_CODE);    }    /**     * 返回带code的信息     * @param code     * @param      * @return     */    public static Result OK(String code){        return new Result(code);    }    /**     * 返回只带code的信息     * @param code     * @param      * @return     */    public static Result faild(String code){        return new Result(code,ConstantUtil.ResponeCode.SYS_ERROR);    }    /**     * 返回带code与提示消息的对象     * @param code     * @param msg     * @param      * @return     */    public static Result faild(String code,String msg){        return new Result(code,msg);    }}
package com.ymy.utils;/** * 常量字典 */public class ConstantUtil {    /**     * 返回状态码信息     */    public static  class  ResponeCode{        /**         * 正常         */       public static final String SUCCESS_CODE="suc-001";        /**         * 系统错误code         */        public static final String SYS_ERROR_CODE="sys_error";        /**         * 系统错误code         */        public static final String NULL_POINTER_EXCEPTION_CODE="Null_Pointer_Exception";        /**         * 系统错误code         */        public static final String NULL_POINTER_EXCEPTION="程序中出现空引用,请检查!";        /**         * 系统错误         */        public static final String SYS_ERROR="网络繁忙,请稍后再试";        /**         * 参数错误code         */        public static final String INVALID_PARAMETER="invalid_parameter";    }    /**     * 日期格式     */    public static  class  Date{        public static final String DATE_FORMATE="yyy-MM-dd HH:mm:ss";    }}

项目地址:https://gitee.com/yq0601/global-exceptions.git

java报错空指针异常_springboot全局异常捕获,真香相关推荐

  1. java报错空指针异常_夯实基础:认识一下这10 个深恶痛绝的 Java 异常

    异常是 Java 程序中经常遇到的问题,我想每一个 Java 程序员都讨厌异常,一 个异常就是一个 BUG,就要花很多时间来定位异常问题. 什么是异常及异常的分类请看这篇文章:异常小结:上一张图搞清楚 ...

  2. java报错空指针异常_分析使用Spring Boot进行单元测试时,报出空指针异常

    使用Spring Boot进行单元测试时,发现使用@Autowired注解的类无法自动注入,当使用这个类的实例的时候,报出NullPointerException,即空指针异常. Spring Boo ...

  3. java报错空指针异常_java – 空指针异常错误,没有明显的代码错误

    我在这里有一个错误,我不知道它来自哪里.我在初学者的 java课程是高中,所以我在这里还没有太多的经验.我有3个相互合并的程序. 我有一个卡片类,可以创建一张扑克牌 //*************** ...

  4. 捕获异常_SpringBoot的异常处理全局异常捕获(图文资料)

    目标:springboot全局异常捕获 概述 1.自定义异常处理 package com.itheima.exeception; /** * @Author David老师 * @Descriptio ...

  5. Java 捕获 mybatis异常_3 springboot集成mybatis和全局异常捕获

    mybatis有两种方式,一种是基于XML,一种是基于注解 springboot集成mybatis 首先先创建表,这里都简化了 DROP TABLE IF EXISTS `user`; CREATE ...

  6. spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获

    spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获 当你的某个控制器内的某个方法报错,基本上回显示出java错误代码,非常不友好,这 ...

  7. SpringBoot配置全局异常捕获

    SpringBoot中自带的异常捕获机制返回的默认页面比较丑,对用户来说不够人性化.所以这篇文章来讲解SpringBoot钟自定义全局异常捕获. 本文的源码已经上传GitHub:https://git ...

  8. Spring 全局异常捕获

    引言 前后端分离开发,后台有时候会出现不可预知的异常(运行时异常),在实际生产中通常需要统一返回符合一定响应结构的异常信息给前端,这一方面可以避免用户看到后台的报错信息,一方面也是保护后端程序免受恶意 ...

  9. 编码技巧——全局异常捕获统一的返回体业务异常

    在开发中,关于异常的捕获曾经是一个头疼的问题:本篇介绍几个方法,如何优雅的捕获处理业务异常: 已检查异常和未检查异常? 先做个介绍,异常Exception分为运行时异常(RuntimeExceptio ...

最新文章

  1. Java基础篇:多线程
  2. MySQL 账户管理
  3. 【C++】43.使用【类对象】与 【类指针】的区别
  4. STM32开发 -- CAN总线详解
  5. linux账号安全小汇
  6. 扩展Java EE应用程序的基础
  7. Myeclipse5.5获取注册码
  8. Sharepoint 2010 根据用户权限隐藏Ribbon菜单
  9. mrsql查询第二高的成绩_及锋而试 锻铁成钢——高二年级部召开第二次考试成绩分析会...
  10. UVALive5379 UVA270 Lining Up【输入输出+水题】
  11. 练习题 - 基于快速文本标题匹配的知识问答实现(二,实现篇)
  12. handlerexceptionresolver ajax,Http请求的异常处理(草稿) (SEUG)
  13. 《JAVA程序设计基础与应用》pdf 附下载链接
  14. 今天再发一下热门关键字,看看能否推广网站
  15. Codeforces 917B MADMAX (DP+博弈)
  16. 8box这件事欠考虑
  17. 【心情】2016ICPC青岛站打铁记
  18. C# dataGridView控件单元格底色 dataGridView背景色 背景色调整 Header背景色前景色
  19. Pycharm问题:this applicatation failed to start because it could not find or laod the qt plaform plugin
  20. 短信验证码校验的实现

热门文章

  1. VUE 项目如何快速优化?| 原力计划
  2. 祝贺 Java 走过创新的 25 年
  3. 面试官:背了几道面试题就敢说熟悉Java源码?我们不招连源码都不会看的人|原力计划...
  4. 女朋友学高数,我花了 15 分钟用栈给她写了一个计算器 | 原力计划
  5. 谁说国产编译器没救了?这个 C/C++ 和 JavaScript 编译器来了 | 程序人生 2020
  6. 真实版“删库跑路”?程序员蓄意破坏线上生产环境!
  7. 如何彻底搞懂面向 Web 开发者的正则表达式?
  8. const 并不能加快 C 代码的运行速度?
  9. 158 行 Python 代码,复现 DeepMind 递归神经网络 DRAW!
  10. 助力开发者的魔法式“绝招儿”,你了解几个?