详解Spring boot/Spring 统一错误处理方案的使用
作者:明人不说暗话___我喜欢你 发布时间:2023-11-24 12:56:07
当我们开发spring web应用程序时,对于如 IOException
, ClassNotFoundException
之类的检查异常,往往编译器会提示程序员采用 try-catch
进行显式捕获,而对于像 ClassCastException
, NullPointerException
这类非检查异常,编译器是不会提示你了,这往往也是能体现程序员代码编写能力的一个方面。
在spring web特别是spring-boot应用中,当一个请求调用成功时,一般情况下会返回 json
格式的对象,就像下面图所示:
但如果请求抛出了一个 RuntimeException
呢?如果我们不做处理,再次调用时将出现下面的页面:
也就是说当调用出现错误时,spring-boot默认会将请求映射到 /error
路径中去,如果没有相应的路径请求处理器,那么就会返回上面的 Whitelabel
错误页面。
1、自定义错误处理页面
当然对运行时异常不做处理是不可能的啦!通常的做法是自定义统一错误页面,然后返回。按照上面的思路,我们实现一个请求路径为 /error
的控制器,控制器返回一个资源路径地址,定义请求映射路径为 /error
的控制器并实现 ErrorController
接口,代码如下:
MyErrorPageController
package com.example.demo.controller.handler.errorpage;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
* The class MyErrorPageController.
*
* Description:自定义错误页面
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@Controller
public class MyErrorPageController implements ErrorController {
@RequestMapping("/error")
public String handleError() {
return "error.html"; // 该资源位于resources/static目录下
}
@Override
public String getErrorPath() {
return null;
}
}
然后在 reosurces/static
目录下建立 error.html
文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>这是个错误页面!存放在resources/static目录下,spring-boot发生错误时默认调用</h1>
</body>
</html>
再次请求 http://localhost:7000/demo/getUserInfoWithNoHandler.json
,如下:
2、使用 @ControllerAdvice
、 @ResponseBody
、 @ExceptionHandler
统一处理异常
在spring中可以使用上面3个注解进行统一异常处理,默认情况下我们可以针对系统中出现的某种类型的异常定义一个统一的处理器handler,比如说系统抛出了一个 NullPointerException
,那么我们可以定义一个专门针对 NullPointerException
的处理器,代码如下:
getUserInfoWithNullPointerException
接口
/**
* 测试空指针错误的处理
* @return
* @throws NullPointerException
*/
@RequestMapping(value = "getUserInfoWithNullPointerException.json", method = RequestMethod.GET)
public Student getUserInfoWithNullPointerException() throws NullPointerException {
throw new NullPointerException();
}
NullPointerExceptionHandler.java
package com.example.demo.controller.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.pojo.ErrorReturn;
/**
*
* The class NullPointerExceptionHandler.
*
* Description:处理空指针
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@ControllerAdvice
public class NullPointerExceptionHandler {
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public ErrorReturn dealNullPointerException() {
e.printStackTrace();
ErrorReturn error = new ErrorReturn();
error.setReturnCode("-1");
error.setDesc("出现空指针异常啦!");
return error;
}
}
浏览器执行: http://localhost:7000/demo/getUserInfoWithNullPointerException.json
同样的道理,如果我们还需要为其他的运行时异常提供统一的处理器,那么也可以像上面一样为每一个异常类型定义一个处理器,比如我们又想为 ArithmeticException
定义处理器,那么我们只需要建立一个类或者方法,然后在方法上的 @ExceptionHanler
注解内加上 ArithmeticException.class
指定异常类型即可。
不过你有没有发现,这样为每种异常类型定义一个异常处理类或者方法,因为运行时异常类型特别多,不可能为每种类型都指定一个处理器类或方法,针对这种情况,spring也是可以解决的。如果我们没有为某种特定类型异常,如 ArithmeticException
定义处理器,那么我们可以定义一个 Exception
或者 Throwable
处理器统一处理。
这样做的好处是,减少了处理器类的数量,同时将异常处理转移到父类上面去,这也是继承的一大优势吧!但是,当你既定义了特定类型的异常,同时又定义了 Exception
异常的处理器,那么要小心了,这里不一定有优先级的关系,也就是说不一定会出现只执行父异常处理器的情况,可能是只执行A处理器,而不执行B处理器或者只执行B处理器,不执行A处理器。如 NullPointerExceptionHandler
异常会向 Exception
异常传递(但 ArithmeticException
不会向 Exception
传递)
现在假设我们既定义上面的 NullPointerExceptionHandler
,又定义了下面的 ExceptionThrowableHandler
,那么当发生 NullPointerException
时,就会默认执行 ExceptionThrowableHandler
的方法。
ExceptionThrowableHandler.java
package com.example.demo.controller.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.pojo.ErrorReturn;
/**
*
* The class ExceptionThrowableHandler.
*
* Description:有些异常会向高级别异常传递(但ArithmeticException不会向Exception传送)
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@ControllerAdvice
public class ExceptionThrowableHandler {
@ExceptionHandler(Throwable.class)
@ResponseBody
public ErrorReturn dealThrowable() {
ErrorReturn error = new ErrorReturn();
error.setDesc("处理Throwable!");
error.setReturnCode("-1");
return error;
}
@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException() {
ErrorReturn error = new ErrorReturn();
error.setReturnCode("-1");
error.setDesc("公共异常处理!");
return error;
}
}
浏览器执行 : http://localhost:7000/demo/getUserInfoWithNullPointerException.json
可以发现只执行 Exception
的处理器,没有执行空指针的处理器,也就是异常处理往上传送了。下面再来看看抛出 ArithmeticException
的情况:
getUserInfoWithArithmeticException.json
/**
* 测试空指针错误的处理
* @return
* @throws NullPointerException
*/
@RequestMapping(value = "getUserInfoWithArithmeticException.json", method = RequestMethod.GET)
public Student getUserInfoWithArithmeticException() throws ArithmeticException {
throw new ArithmeticException();
}
ArithmeticExceptionHandler.java
package com.example.demo.controller.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.pojo.ErrorReturn;
@ControllerAdvice
public class ArithmeticExceptionHandler {
/**
* 处理ArithmeticException异常
* @return
*/
@ResponseBody
@ExceptionHandler(ArithmeticException.class)
public ErrorReturn dealArithmeticException() {
ErrorReturn errorObject = new ErrorReturn();
errorObject.setReturnCode("-1");
errorObject.setDesc("算数处理出现异常!");
return errorObject;
}
}
浏览器执行 : http://localhost:7000/demo/getUserInfoWithArithmeticException.json
结果发现异常处理并没有往上层的 ExceptionHandler
传送。
总结:对于既定义特定类型的处理器,又定义 Exception
等父类型的处理器时要特别小心,并不是所有的异常都会往上级处理,如果我们想只减少处理器类的数量,不想为每种特定类型的处理器添加类或者方法,那么小编建议使用 instanceof
关键字对异常类型进行判断即可。
如下面的代码,我们只建立一个公共的异常处理器,处理 Exception
异常,同时使用 instanceof
进行判断。
@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException(Exception e) {
ErrorReturn error = new ErrorReturn();
// 此处可以采用 instanceof 判断异常类型
if (e instanceof ArithmeticException) {
error.setReturnCode("-1");
error.setDesc("算数异常处理!");
return error;
}
System.err.println("exception");
error.setReturnCode("-1");
error.setDesc("公共异常处理!");
return error;
}
浏览器执行抛出 ArithmeticException
的接口,如下:
本文代码地址: https://github.com/SmallerCoder/spring_exceptionHandler
来源:https://juejin.im/post/5b2101716fb9a01e80785be2
猜你喜欢
- 一、前言最近在看android fragment与Activity进行数据传递的部分,看到了接口回调的内容,今天来总结一下。二、回调的含义和
- 前几天在跟公司大佬讨论一个问题时,看到他使用Handler的一种方式,旁边的同事在说:以前不是这么用的啊。这个问题引发了我的好奇,虽然当时翻
- 在Java中创建一个线程有两种方法:继承Thread类和实现Runnable接口。下面通过两个例子来分析两者的区别:1)继承Thread类p
- 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解。 首先
- springboot嵌套子类使用在实际项目里,我们会使用到一个User用户含有子类Address、这种嵌套子类在开发中会遇到很多问题,现在主
- 调用和回调机制在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种:1.同步调用同步调用是最基本并且最简
- 一. Window 分类应用 Window(ApplicationWindow: 对应一个 Acitivity)
- trim中prefix与suffix等标签用法1.prefix 前缀增加的内容2.suffix 后缀增加的内容3.prefixOverrid
- /** * 进行BigDecimal对象的加减乘除,四舍五入等运算的工具类 * * @author Marydon * @createTi
- maven3 安装:安装 Maven 之前要求先确定你的 JDK 已经安装配置完成。Maven是 Apache 下的一个项目,目前最新版本是
- Executor接口基于以下方法可以完成增,删,改查以及事务处理等操作。事实上,mybatis中的所有数据库操作是通过调用这些方法实现的。p
- 所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说,这个B便叫做回调方法。下面看
- 最近做的项目,需要将一些信息导出到word中。在网上找了好多解决方案,现在将这几天的总结分享一下。目前来看,java导出word大致有6种解
- WCF实例(带步骤) <xmlnamespace prefix ="o" ns ="urn:schema
- 递归生成一个如图的菜单,编写两个类数据模型Menu、和创建树形的MenuTree。通过以下过程实现:1.首先从菜单数据中获取所有根节点。2.
- 构造方法以及参数:PageView可用于Widget的整屏滑动切换,如当代常用的短视频APP中的上下滑动切换的功能,也可用于横向页面的切换,
- 线程状态NEW:刚创建未启动的线程RUNNABLE:正在执行状态BLOCKED:处于阻塞状态的线程WAITING:正在等待另一个线程执行特定
- 如下所示:package cn.sunzn.md5;import java.security.MessageDigest;import ja
- 首先使用PImage来实例化对象,再通过loadImage赋值,两层for循环遍历图片上的像素点,每隔5个像素点,画一个直径为3的圆。颜色通
- 本文实例讲述了java实现单词搜索迷宫游戏。分享给大家供大家参考。具体分析如下:我们在杂志上,经常能够看到找单词的小游戏,在一个二维表格中,