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. 第六章 类(Class) 和对象(Object)

    一.笔记导图 二.实例代码: public class PrintCarStatus{ public static void main(String[] args){ int speed; Strin ...

  2. 基于ECS搭建云上博客

    场景介绍 本文为您介绍如何基于ECS搭建云上博客. 背景知识 本场景主要涉及以下云产品和服务: 云服务器ECS 云服务器(Elastic Compute Service,简称ECS)是阿里云提供的性能 ...

  3. Parcelable使用(二)

    简单的Activity间数据传递用Intent,复杂的用Parcelable,举个栗子第一个activity写入Parcel的String类型的name和int类型的age,第二个activity取出 ...

  4. SQL SERVER迁移--更换磁盘文件夹

    默认情况下SQL SERVER的安装路径与数据库的默认存放路径是在C盘的--这就很尴尬. 平时又不注意,有天发现C盘的剩余空间比较吃紧了,于是着手想办法迁移文件夹. 一.环境准备 数据库版本--SQL ...

  5. 联考day7 C. 树和森林 树形DP

    题目描述 样例 样例输入 8 5 BBWWWBBW 1 2 2 3 4 5 6 7 7 8 样例输出 84 2 1 4 样例解释 分析 首先,我们要预处理出一个点到该联通块内所有点的距离之和 \(f\ ...

  6. Longest common subsequence(LCS)

    问题 说明该问题在生物学中的实际意义 Biological applications often need to compare the DNA of two (or more) different ...

  7. python解决百钱买百鸡

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

  8. [Luogu P1066] 2^k进制数 (组合数或DP)

    题面 传送门:https://www.luogu.org/problemnew/show/P1066 Solution 这是一道神奇的题目,我们有两种方法来处理这个问题,一种是DP,一种是组合数. 这 ...

  9. Spider Storage Engine

    这个引擎可以完成MySQL的数据库分片

  10. hashmap源码面试分析

    HashMap源码问题解析 问: 说一说对hash的理解 答: hash是对任意长度的输入输出为相同长度的输出 问: hash算法的问题 答: hash冲突问题 问: hash冲突是否可以避免 答: ...