当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处理这些异常呢?

本文将介绍一种全局异常处理方式,主要包括以下知识点

  • @ControllerAdvice Controller 增强
  • @ExceptionHandler 异常捕获
  • @ResponseStatus 返回状态码
  • NoHandlerFoundException 处理(404 异常捕获)

右键查看原文: SpringBoot系列教程web篇之全局异常处理

I. 环境搭建

首先得搭建一个 web 应用才有可能继续后续的测试,借助 SpringBoot 搭建一个 web 应用属于比较简单的活;

创建一个 maven 项目,pom 文件如下

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7</version>
<relativePath/> <!-- lookup parent from update -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
</dependencies> <build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

依然是一般的流程,pom 依赖搞定之后,写一个程序入口

/**
* Created by @author yihui in 15:26 19/9/13.
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}

II. 异常处理

1. @ControllerAdvice

我们通常利用@ControllerAdvice配合注解@ExceptionHandler来实现全局异常捕获处理

  • @ControllerAdvice为所有的 Controller 织入增强方法
  • @ExceptionHandler标记在方法上,表示当出现对应的异常抛出到上层时(即没有被业务捕获),这个方法会被触发

下面我们通过实例进行功能演示

a. 异常捕获

我们定义两个异常捕获的 case,一个是除 0,一个是数组越界异常

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler { public static String getThrowableStackInfo(Throwable e) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
e.printStackTrace(new java.io.PrintWriter(buf, true));
String msg = buf.toString();
try {
buf.close();
} catch (Exception t) {
return e.getMessage();
}
return msg;
} @ResponseBody
@ExceptionHandler(value = ArithmeticException.class)
public String handleArithmetic(HttpServletRequest request, HttpServletResponse response, ArithmeticException e)
throws IOException {
log.info("divide error!");
return "divide 0: " + getThrowableStackInfo(e);
} @ResponseBody
@ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
public String handleArrayIndexOutBounds(HttpServletRequest request, HttpServletResponse response,
ArrayIndexOutOfBoundsException e) throws IOException {
log.info("array index out error!");
return "aryIndexOutOfBounds: " + getThrowableStackInfo(e);
}
}

在上面的测试中,我们将异常堆栈返回调用方

b. 示例服务

增加几个测试方法

@Controller
@RequestMapping(path = "page")
public class ErrorPageRest { @ResponseBody
@GetMapping(path = "divide")
public int divide(int sub) {
return 1000 / sub;
} private int[] ans = new int[]{1, 2, 3, 4}; @ResponseBody
@GetMapping(path = "ary")
public int ary(int index) {
return ans[index];
}
}

c. 测试说明

实例测试如下,上面我们声明捕获的两种异常被拦截并输出对应的堆栈信息;

但是需要注意

  • 404 和未捕获的 500 异常则显示的 SpringBoot 默认的错误页面;
  • 此外我们捕获返回的 http 状态码是 200

2. @ResponseStatus

上面的 case 中捕获的异常返回的状态码是 200,但是在某些 case 中,可能更希望返回更合适的 http 状态码,此时可以使用ResponseStatus来指定

使用方式比较简单,加一个注解即可

@ResponseBody
@ExceptionHandler(value = ArithmeticException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleArithmetic(HttpServletRequest request, HttpServletResponse response, ArithmeticException e)
throws IOException {
log.info("divide error!");
return "divide 0: " + getThrowableStackInfo(e);
}

3. 404 处理

通过@ControllerAdvice配合@ExceptionHandler可以拦截 500 异常,如果我希望 404 异常也可以拦截,可以如何处理?

首先修改配置文件application.properties,将NoHandlerFoundException抛出来

# 出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
# 设置静态资源映射访问路径,下面两个二选一,
spring.mvc.static-path-pattern=/statics/**
# spring.resources.add-mappings=false

其次是定义异常捕获

@ResponseBody
@ExceptionHandler(value = NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public String handleNoHandlerError(NoHandlerFoundException e, HttpServletResponse response) {
return "noHandlerFound: " + getThrowableStackInfo(e);
}

再次测试如下,404 被我们捕获并返回堆栈信息

II. 其他

0. 项目

web 系列博文

项目源码

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

SpringBoot系列教程web篇之全局异常处理的更多相关文章

  1. SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver

    关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理: 本篇博文则带来另外一种并不常见的使用方式,通过实 ...

  2. SpringBoot系列教程web篇之过滤器Filter使用指南扩展篇

    前面一篇博文介绍了在 SpringBoot 中使用 Filter 的两种使用方式,这里介绍另外一种直接将 Filter 当做 Spring 的 Bean 来使用的方式,并且在这种使用方式下,Filte ...

  3. SpringBoot系列教程Web篇之开启GZIP数据压缩

    本篇可以归纳在性能调优篇,虽然内容非常简单,但效果可能出乎预料的好: 分享一个真实案例,我们的服务部署在海外,国内访问时访问服务时,响应有点夸张:某些返回数据比较大的接口,耗时在 600ms+上,然而 ...

  4. SpringBoot系列教程web篇Listener四种注册姿势

    java web三要素Filter, Servlet前面分别进行了介绍,接下来我们看一下Listener的相关知识点,本篇博文主要内容为SpringBoot环境下,如何自定义Listener并注册到s ...

  5. SpringBoot系列教程web篇Servlet 注册的四种姿势

    原文: 191122-SpringBoot系列教程web篇Servlet 注册的四种姿势 前面介绍了 java web 三要素中 filter 的使用指南与常见的易错事项,接下来我们来看一下 Serv ...

  6. SpringBoot系列教程web篇之过滤器Filter使用指南

    web三大组件之一Filter,可以说是很多小伙伴学习java web时最早接触的知识点了,然而学得早不代表就用得多.基本上,如果不是让你从0到1写一个web应用(或者说即便从0到1写一个web应用) ...

  7. SpringBoot系列教程web篇之404、500异常页面配置

    接着前面几篇web处理请求的博文,本文将说明,当出现异常的场景下,如404请求url不存在,,403无权,500服务器异常时,我们可以如何处理 原文友链: SpringBoot系列教程web篇之404 ...

  8. SpringBoot系列教程web篇之重定向

    原文地址: SpringBoot系列教程web篇之重定向 前面介绍了spring web篇数据返回的几种常用姿势,当我们在相应一个http请求时,除了直接返回数据之外,还有另一种常见的case -&g ...

  9. SpringBoot系列教程web篇之如何自定义参数解析器

    title: 190831-SpringBoot系列教程web篇之如何自定义参数解析器 banner: /spring-blog/imgs/190831/logo.jpg tags: 请求参数 cat ...

随机推荐

  1. MapTask工作机制

    (1)Read阶段:MapTask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value. (2)Map阶段:该节点主要是将解析出的key/value交给用 ...

  2. bytes与网络通信

    字符串是人机交互的符号: bytes是最接近计算机本身的信息表示方法. 网络通信是计算机与计算机之间的通信. 所有的通信信息,必须转化为bytes流的方式在计算机间传递. bytes与数据类型无关,与 ...

  3. Struts2框架的搭建

    Struts2是WebWork框架的升级版本,替代了Servlet. 由于用IDEA下载jar包失败,直接创建手动导包. 1.导包: (1)Struts2的目录结构: (2)导入jar包: 2.书写A ...

  4. 1.Http讲解

    1.什么是HTTP HTTP(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP上. 文本:html,字符串,.... 超文本:图片,音乐,视频,定位,地图... 80端口 HTTPS:安 ...

  5. 大厂HR面试必备ES6中的深入浅出面试题知识点

    ESMAScript6简介,ES6是JavaScript语言的下一代标准,目的是让JavaScript语言可以写复杂的大型应用程序,成为企业级语言.那么ECMAScript和JavaScript的关系 ...

  6. Zabbix实战-简易教程--中间件RabbitMQ监控

    一.环境 zabbix版本:3.0 二.脚本说明 .├── rabbitmq.template.xml   模板文件├── scripts  │   └── rabbitmq│   ├── api.p ...

  7. 19 条效率至少提高 3 倍的 MySQL 技巧

    阅读本文大概需要 4 分钟. 来源:https://zhuanlan.zhihu.com/p/49888088 本文我们来谈谈项目中常用的 MySQL 优化方法,共 19 条,具体如下: 1.EXPL ...

  8. 阿里云服务器25端口禁用之如何使用Java发邮件(解决25端口禁用问题)

    通常我们在本地使用Java发送邮件,通常是没有问题的,但是部署到服务器的话,就可能遇到问题.当然了,这与运营商也有关系.比如我之前在咖啡主机上购买虚拟机,然后将个人网站部署上去,通常是没有问题的,没有 ...

  9. ansible-playbook-批量修改主机名

    修改cat /etc/ansible/hosts [test]10.27.235.108 host_name=test_host_name - hosts: test user: root gathe ...

  10. Http的状态码及状态码的类型

    Http的状态码以及根据这些状态码分成5种类型 statusCode/100 /* * Copyright 2002-2013 the original author or authors. * * ...