关于SpringBoot如何处理全局异常的思考
2020/2/25 17:15:43
本文主要是介绍关于SpringBoot如何处理全局异常的思考,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
背景
日常开发中经常会遇到各种系统异常(Exception),但是将异常堆栈直接返回给页面却是很不友好的一种做法,于是在我们日常的项目开发中,个人思考总结了一种自认为还不错的全局异常处理方案义工参考,欢迎指点意见和支持。
基本思路
这个方案主要用到了深度优先的递归循环、策略模式和基于枚举的单例模式,针对每种不同类型异常单独实现异常处理逻辑,实际中大多也是如此。
异常在抛出的时候有两种情况:一是没有子异常(getCause() == null
),另一种是有子异常。深度优先指的是优先以抛出异常的源头为准进行处理,比如项目内部抛出了一个NullPointerException
,然后外层catch
块捕获后重新包装成IllegalArgumentException
,那么NPE
就是“异常的源头”,那么最终就是处理NPE
后返回给前端。
策略模式只的是针对每种异常单独注册处理逻辑,放到HashMap
里,处理的时候直接用getClass()
获取对应的逻辑处理并返回即可。
具体实现
- 首先定义一个异常处理器接口用来标记处理
Exception
:
public interface ExceptionMessageConverter<T extends Throwable> { /** * 对返回值类型不需要什么要求 * 返回 ModelAndView 或者普通 POJO 都可以 * SpringMVC 有完善的处理机制,返回 json 或 View */ Object onThrow( HttpServletRequest request, HttpServletResponse response, T exception ); } 复制代码
- 异常处理器注册器:
ExceptionRegistry
public final class ExceptionRegistry { // 这是项目启动就会写入的,没必要用 ConcurrentHashMap final statis Map<Class, ExceptionMessageConverter> cached = new HashMap<>(); public static void registry( Class<? extends Throwable> type, ExceptionMessageConverter converter ) { cached.put(type, converter); } public static ExceptionMessageConverter get(Class type){ return cached.get(type); } } 复制代码
- 预定义异常处理器,这里用到了基于枚举的单例模式
public enum PredefinitionExceptionConverterEnum implements ExceptionMessageConverter { onBindException(BindException.class) { @Override public Object onThrow( HttpServletRequest request, HttpServletResponse response, Throwable exception ) { // ... 自定义处理逻辑 return new ResponseEntity("长度至少为 6", HttpStatus.BAD_REQUEST); } }, onConstraintViolationException(ConstraintViolationException.class) { // 省略实现... }, /* ... 自定义更多处理逻辑 */ ; PredefinitionExceptionConverterEnum(Class type) { // 在构造器里面向 ExceptionRegistry 注册 ExceptionRegistry.registry(type, this); } } 复制代码
- 异常处理器:GlobalExceptionAdvice
@RestControllerAdvice public class GlobalExceptionAdvice { public static Object doThrowable( HttpServletRequest request, HttpServletResponse response, Throwable root, Throwable ex ) { Throwable current = ex, cause; ExceptionMessageConverter converter = null; if ((cause = current.getCause()) != null) { // 如果有子异常优先以子异常为准 Object result = onThrowable(request, response, root, cause); if (result != null) { return result } } if ((converter = ExceptionRegistry.get(current.getClass())) != null) { // 如果子异常不存在处理器,就处理当前异常 return handler.onThrowable(request, response, current); } return null; } /** * 捕获并交给 doThrowable 处理异常 * 注意这里的返回值是 ResponseEntity, * 这是我们项目的实现,效果类似 @ResponseBody * 返回的 content-type 是 application/json */ @ExceptionHandler(Throwable.class) public ResponseEntity onThrowable( HttpServletRequest, request, HttpServletResponse response, Throwable ex ) throws Throwable { Object result = doThrowable(ex, ex); if (result == null) { // 如果一个匹配的处理器都没有,走这个 return new ResponseEntity("系统内部异常", HttpStauts.INTERNAL_SERVER_ERROR); } return result; } } 复制代码
后记
至此,一个自定义全局异常处理就完成了,但是还有些想要说明的地方
- ExceptionRegistry 及所有方法之所以定义成
public
是为了在PredefinitionExceptionConverterEnum
之外仍然可以自定义和注册其他处理器; - 我们项目没有用到其他项目通常都会定义的
ResultBody
里面包括data
、status
、errorCode
等返回体,因为我们项目都尽可能用http
状态码返回,也可能是这个项目不够大,还用不着其他状态码,总之,这基础上目前是够用的,以及将来如果要扩展自定义状态码也和前端预定好方案了,前后端架构层面已经预留有扩展空间。 - 上面列出各种看似牛哄哄的名词各位不要太在意哈,我只是实现完这个处理策略后,这个区归纳的它,错误还请斧正!!!
罗列一部分常见的异常类型:
BindException
: Spring MVC 绑定数据时通过@NotBlank
相关注解引发的表单验证错误;ConstraintViolationException
: 这个还是参数验证错误;MissingServletRequestParameterException
:这个是请求参数里缺少某些参数;MethodArgumentNotValidException
:这个是被@RequestBody
和@Valid
注解的验证错误TransientObjectException
:这个是hibernate
级联保存的异常(我们项目用得是jpa
),一个实体在保存时所依赖的其他实体应该被持久化,否则就抛这个异常。EntityNotFoundException
:顾名思义,实体不存在,还是jpa
的异常;SQLSyntaxErrorException
:sql 语法错误;SQLIntegrityConstraintViolationException
:外键关联错误;MySQLTransactionRollbackException
:顾名思义,MySQL
事务回滚错误;SQLException
:不解释MalformedURLException
:URL 格式错误,网络请求时引发。
这篇关于关于SpringBoot如何处理全局异常的思考的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27JavaScript面试真题详解与解答
- 2024-12-27掌握JavaScript大厂面试真题:新手入门指南
- 2024-12-27JavaScript 大厂面试真题详解与解析
- 2024-12-26网络攻防资料入门教程
- 2024-12-26SQL注入资料详解:入门必读教程
- 2024-12-26初学者指南:数据库服务漏洞项目实战
- 2024-12-26网络安全项目实战:新手入门指南
- 2024-12-26网络攻防项目实战入门教程
- 2024-12-26信息安全项目实战:从入门到初步应用
- 2024-12-26SQL注入项目实战:初学者指南