当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处理这些异常呢?
本文将介绍一种全局异常处理方式,主要包括以下知识点
右键查看原文: SpringBoot系列教程web篇之全局异常处理
首先得搭建一个 web 应用才有可能继续后续的测试,借助 SpringBoot 搭建一个 web 应用属于比较简单的活;
创建一个 maven 项目,pom 文件如下
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7version>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASEspring-cloud.version>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.45version>
dependency>
dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
pluginManagement>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
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);
}
}
我们通常利用@ControllerAdvice
配合注解@ExceptionHandler
来实现全局异常捕获处理
@ControllerAdvice
为所有的 Controller 织入增强方法@ExceptionHandler
标记在方法上,表示当出现对应的异常抛出到上层时(即没有被业务捕获),这个方法会被触发下面我们通过实例进行功能演示
我们定义两个异常捕获的 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);
}
}
在上面的测试中,我们将异常堆栈返回调用方
增加几个测试方法
@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];
}
}
实例测试如下,上面我们声明捕获的两种异常被拦截并输出对应的堆栈信息;
但是需要注意
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5RvTZFUc-1570790075351)(http://spring.hhui.top/spring-blog/imgs/191010/00.jpg)]
上面的 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);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cqZk9uui-1570790075359)(http://spring.hhui.top/spring-blog/imgs/191010/01.jpg)]
通过@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 被我们捕获并返回堆栈信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0NsQGUii-1570790075366)(http://spring.hhui.top/spring-blog/imgs/191010/02.jpg)]
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8w2duusI-1570790075367)(https://spring.hhui.top/spring-blog/imgs/info/info.png)]