SpringBoot异常处理

简介

​ 日常开发过程中,难免有的程序会因为某些原因抛出异常,而这些异常一般都是利用try ,catch的方式处理异常或者throw,throws的方式抛出异常不管。这种方法对于程序员来说处理也比较麻烦,对客户来说也不太友好,所以我们希望既能方便程序员编写代码,不用过多的自己去处理各种异常编写重复的代码又能提升用户的体验,这时候全局异常处理就显得很重要也很便捷了,是一种不错的选择。

一、 全局异常捕获与处理

Springboot对于异常的处理做了不错的支持,它提供了两个可用的注解。

@ControllerAdvice:用来开启全局的异常捕获

@ExceptionHandler:说明捕获哪些异常,对哪些异常进行处理。

@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value =Exception.class)
public String exceptionHandler(Exception e){
System.out.println("发生了一个异常"+e);
return e.getMessage();
}
}

  

上面这段代码的意思是,只要是代码运行过程中有异常就会进行捕获,并输出出这个异常。然后我们随便编写一个会发生异常的代码,测试出来的异常是这样的。

 

​ 这对于前后端分离来说并不好,前后端分离之后唯一的交互就是json了,我们也希望将后端的异常变成json返回给前端处理,所以就需要统一结果返回和统一异常处理。

二、统一结果返回与统一异常

Result类:封装返回结果。

public class Result<T> {
private Integer code;//状态码
private String message;//提示消息
private T data;//数据 public Result() {
} /**
* @param code 响应码
* @param message 响应信息
*/
public Result(Integer code, String message) {
this.code = code;
this.message = message;
} /**
* @param code 响应码
* @param message 响应信息
* @param data 数据
*/
public Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
} /**
* @param resultEnum 自定义枚举类,包含 code 和 message
*/
public Result(ResultEnum resultEnum) {
this.code = resultEnum.getCode();
this.message = resultEnum.getMessage();
} /**
* @param resultEnum 自定义枚举类,包含 code 和 message
* @param data 数据
*/
public Result(ResultEnum resultEnum, T data) {
this.code = resultEnum.getCode();
this.message = resultEnum.getMessage();
this.data = data;
} /**
* 自定义异常返回的结果
* @param definitionException 自定义异常处理类
* @return 返回自定义异常
*/
public static Result<Object> defineError(DefinitionException definitionException) {
return new Result<>(definitionException.getErrorCode(), definitionException.getErrorMessage());
} /**
* 其他异常处理方法返回的结果
* @param resultEnum 自定义枚举类,包含 code 和 message
* @return 返回其他异常
*/
public static Result<Object> otherError(ResultEnum resultEnum) {
return new Result<>(resultEnum);
} //这里写get和set方法
}

  

注意:其中省略了get,set方法。

ResultEnum:自定义枚举类。

public enum ResultEnum {
// 数据操作定义
SUCCESS(200, "成功"),
TIME_OUT(130, "访问超时"),
NO_PERMISSION(403, "拒绝访问"),
NO_AUTH(401, "未经授权访问"),
NOT_FOUND(404, "无法找到资源"),
METHOD_NOT_ALLOWED(405, "不支持当前请求方法"),
SERVER_ERROR(500, " 服务器运行异常"),
NOT_PARAM(10001, "参数不能为空"),
NOT_EXIST_USER_OR_ERROR_PASSWORD(10002, "该用户不存在或密码错误"),
NOT_PARAM_USER_OR_ERROR_PASSWORD(10003, "用户名或密码为空");;
/**
* 响应码
*/
private final Integer code; /**
* 响应信息
*/
private final String message; /**
* 有参构造
* @param code 响应码
* @param message 响应信息
*/
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
} public Integer getCode() {
return code;
} public String getMessage() {
return message;
}
}

  

注意:枚举类中定义了常见的错误码以及错误的提示信息。这里我们就定义好了统一的结果返回,其中里面的静态方法是用来当程序异常的时候转换成异常返回规定的格式。

DefinitionException:自定义异常处理类。

//@ControllerAdvice+@ResponseBody,开启全局的异常捕获,返回JSON
@RestControllerAdvice
public class GlobalExceptionHandler { /**
* 处理自定义异常
* @return Result
* @ExceptionHandler 说明捕获哪些异常,对那些异常进行处理。
*/
@ExceptionHandler(value = DefinitionException.class)
public Result<Object> customExceptionHandler(DefinitionException e) {
return Result.defineError(e);
} /**
* 处理其他异常
* @return Result
*/
@ExceptionHandler(value = Exception.class)
public Result<Object> exceptionHandler(Exception e) {
return Result.otherError(ErrorEnum.INTERNAL_SERVER_ERROR);
}
}

  

说明:将对象解析成json,是为了方便前后端的交互。

三、代码测试与结果

测试类

ResultController:测试的controller类

@RestController
public class ResultController { //获取学生信息
@GetMapping("/student")
public Result<Student> getStudent() {
Student student = new Student();
student.setId(1);
student.setAge(18);
student.setName("XuWwei")
return new Result<>(ResultEnum.SUCCESS, student);
} //自定义异常处理
@RequestMapping("/getDeException")
public Result<Object> DeException() {
throw new DefinitionException(400, "我出错了");
} //其他异常处理
@RequestMapping("/getException")
public Result Exception(){
Result result = new Result();
int a=1/0;
return result;
}

  

Student:学生类

public class Student {
/**
* 唯一标识id
*/
private Integer id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
}

  

注意:其中省略了get,set方法。

测试结果

启动项目,一个一个测试

  1. 正常测试

     

可以看到数据是正常返回json,没有异常。

  1. 自定义异常
 

可以看到这个自定义的异常被捕获到了,并且返回了一个json。

  1. 其他异常

     

可以看到这个异常被捕获到了,并且返回了一个json。

注意:这种方法是不能处理404异常的,捕获不到。

四、404异常特殊处理

1、修改配置文件

​ 默认情况下,SpringBoot是不会抛出404异常的,所以@ControllerAdvice也不能捕获到404异常。我们可以通过配置文件来让这个注解能捕获到404异常,在application.properties中添加以下配置:

#当发现404异常时直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
#关闭默认的静态资源路径映射,这样404不会跳转到默认的页面
spring.resources.add-mappings=false

  

但是关闭默认的静态资源路径映射会让静态资源访问出现问题,也就是不适合前后端一体的情况。

但是我们可以手动配置静态资源路径映射,就能正常访问静态资源了。

@Configuration
public class ResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//可以访问localhost:8080/static/images/image.jpg
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}

  

2、修改error跳转路径

​ 关闭默认的静态资源路径映射显然不太合理,可能会导致其他的错误发生,所以也可以通过修改默认错误页面的跳转路径来达到我们的目的。

在GlobalExceptionHandler类中添加NotFoundExceptionHandler类,这个类继承了ErrorController,可以重写error的跳转路径。

//处理404NotFoundException
@Controller
class NotFoundExceptionHandler implements ErrorController { //设置错误页面路径
@Override
public String getErrorPath() {
return "/error";
} //当访问error路径时,返回一个封装的异常的Json
@RequestMapping("/error")
@ResponseBody
public Result<Object> error() {
return Result.otherError(ResultEnum.NOT_FOUND);
}
}

  

五、拓展异常类

​ GlobalExceptionHandler的exceptionHandler方法将所有的异常统一返回500系统错误,这不符合我们的设想,所以我们可以通过判断异常的类型,来返回不同的值。

将exceptionHandler改成以下代码:

/**
* 处理其他异常
* @return Result
*/
@ExceptionHandler(value = Exception.class)
public Result<Object> exceptionHandler(Exception e) {
if (e instanceof NullPointerException){
//捕获空指针异常
return Result.otherError(ResultEnum.NOT_PARAM);
}else if (e instanceof IllegalAccessException){
//非法访问异常
return Result.otherError(ResultEnum.NO_PERMISSION);
} else{
return Result.otherError(ResultEnum.SERVER_ERROR);
}
}

  

注意:更多异常可以通过else if来细分。

六、总结

​ springboot的异常处理,需要通过@ControllerAdvice注解以及 @ExceptionHandler注解,来拦截所有的异常,并通过一个封装返回值返回。但是,这两个注解无法捕获404NotFound异常,因为SpringBoot默认是不会抛出404异常的,所以要通过继承ErrorController来修改404异常的跳转路径,达到捕获404异常的目的。

深度分析:SpringBoot异常捕获与封装处理,看完你学会了吗?的更多相关文章

  1. SpringBoot源码篇:深度分析SpringBoot如何省去web.xml

    一.前言 从本博文开始,正式开启Spring及SpringBoot源码分析之旅.这可能是一个漫长的过程,因为本人之前阅读源码都是很片面的,对Spring源码没有一个系统的认识.从本文开始我会持续更新, ...

  2. 深度分析:面试腾讯,阿里面试官都喜欢问的String源码,看完你学会了吗?

    前言 最近花了两天时间,整理了一下String的源码.这个整理并不全面但是也涵盖了大部分Spring源码中的方法.后续如果有时间还会将剩余的未整理的方法更新到这篇文章中.方便以后的复习和面试使用.如果 ...

  3. 深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

  4. 深度分析:mybatis的底层实现原理,看完你学会了吗?

    前言 最近在和粉丝聊天的时候被粉丝问到jdbc和mybatis底层实现这一块的问题,而且还不止一个小伙伴问到,于是我似乎认识到了问题的严重性,我花了两天时间整理了一下自己的认识和网上查阅的资料写了这篇 ...

  5. SpringBoot图文教程15—项目异常怎么办?「跳转404错误页面」「全局异常捕获」

    有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1-Spr ...

  6. springBoot系列教程07:异常捕获

    发生异常是很正常的事,异常种类也是千奇百怪,发生异常并不可怕,只要正确的处理,并正确的返回错误信息并无大碍,如果不进行捕获或者处理,分分钟服务器宕机是很正常的事 所以处理异常时,最基本的要求就是发生异 ...

  7. springboot(二 如何访问静态资源和使用模板引擎,以及 全局异常捕获)

    在我们开发Web应用的时候,需要引用大量的js.css.图片等静态资源. 默认配置 Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则: /static / ...

  8. 4、springboot之全局异常捕获

    1.新建一个springboot项目 添加一个全局异常的类 import org.springframework.web.bind.annotation.ControllerAdvice; impor ...

  9. springboot 全局异常捕获,异常流处理业务逻辑

    前言 上一篇文章说到,参数校验,往往需要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的.参数异常springboot默认的返回结构: { "timestamp": ...

随机推荐

  1. Windos--jar包注册成服务

    1.下载资源 链接: https://pan.baidu.com/s/16asJXGudsRN23Rwra_qGZw 提取码: w2gv 解压后有五个文件 1.1注意事项 1.把你的生成的jar包放入 ...

  2. 关于Python的面相对象编程

    Python 其实不是面向对象的语言,更像是C语言的面向过程编程的语言 但 Python 也支持 class 关键字来实现类的声明与创建 但 Python 的对象更像是 JavaScript 的函数 ...

  3. [String] intern()方法

    intern()方法设计的初衷,就是重用String对象,以节省内存消耗. JDK1.6以及以前版本中,常量池是放在 Perm 区(属于方法区)中的,熟悉JVM的话应该知道这是和堆区完全分开的. 使用 ...

  4. B. Nauuo and Circle 解析(思維、DP)

    Codeforce 1172 B. Nauuo and Circle 解析(思維.DP) 今天我們來看看CF1172B 題目連結 題目 略,請直接看原題 前言 第一個該觀察的事情一直想不到,看了解答也 ...

  5. 编写优美代码的七条规范(Python版)

    编程是一种与计算机系统通信的语言.交流就是与某人分享思想.二进制语言是计算机的最核心的语言.但是在前端,我们有很多种语言.这些语言使用解释器将前端代码转换为二进制代码.基本上,系统对一行代码执行大量内 ...

  6. python解决百钱买百鸡

    百钱买百鸡 关注公众号"轻松学编程"了解更多. 现有100钱,公鸡5文钱一只,母鸡3文钱一只,小鸡一文钱3只 要求:公鸡.母鸡,小鸡都要有,把100文钱花完,买的鸡的数量正好是10 ...

  7. 循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

    在我们使用Vue+Element开发前端的时候,往往涉及到很多界面组件的使用,其中很多直接采用Element官方的案例即可,有些则是在这个基础上封装更好利用.更少代码的组件:另外有些则是直接采用第三方 ...

  8. glog修改

    在写代码的过程中,打log肯定是少不了的,毕竟不能总靠调试来发现问题.log库的选用就很纠结了,成熟的log库非常多,log4cpp.log4cxx.poco.log.boost.log.glog等等 ...

  9. 关于synchronized无法锁住Integer原因

    原因 在多线程的时候,为了保证数据安全,必须在修改数据时使用线程同步,java中的synchronized用来实现线程同步.线程列队. 学完多线程基础的我,写一个多线程交替输出1,2,3,4,5... ...

  10. linux中配置yum文件

    yum简介:yum的宗旨是自动化地升级,安装/移除rpm包,收集rpm包的相关信息,检查依赖性并自动提示用户解决. yum的关键之处是要有可靠的repository,顾名思义,这是软件的仓库,它可以是 ...