01、背景介绍
在上文中,我们介绍了在 Spring Boot 中实现接口数据格式的统一返回处理实现,其中就包括程序运行时的异常处理,通过全局异常处理器,可以简化代码逻辑,统一响应格式。
其实在 Spring Boot 中,针对controller层的异常处理有很多种办法。今天通过这篇文章,我们就一起来总结一下相关异常处理的实现方式。
02、方案实践
在 Spring Boot 中针对controller层的异常处理,有两种常用实现方式,都可以达到简化代码逻辑的效果。
- 方式一:通过@ControllerAdvice和@ExceptionHandler注解实现全局异常的处理
- 方式二:通过实现HandlerExceptionResolver接口来完成全局异常的处理
下面我们一起来看看具体实现。
2.1、全局异常处理方式一
通过@ControllerAdvice和@ExceptionHandler注解实现全局异常拦截,在之前的文章中我们有多次介绍过,它可以拦截controller层请求方法抛出的异常信息,同时外加@ ResponseBody注解,可以实现响应类型为json格式。
例如,现在有两种异常类型NullPointerException和Exception,分别对其进行捕捉,具体实现如下!
测试代码,如下:
启动服务后,在浏览器中请求http://localhost:8080/add,结果如下:
图片
请求http://localhost:8080/delete,结果如下:
图片
结果与预期一致。
2.1.1、自定义异常类实现
很多场景下,我们希望通过自定义异常类来返回相关错误信息,如何实现呢?
首先自定义一个异常类CustomerException。
然后,在全局异常处理器中增加相关的捕捉方法。
测试代码,如下:
启动服务后,在浏览器中请求http://localhost:8080/update,结果如下:
图片
结果与预期一致!
2.1.2、404 异常特殊处理
默认情况下,@ExceptionHandler注解无法捕捉到 404 异常,比如请求一个无效的地址,返回信息如下:
图片
如果想要捕捉到这种异常,可以在application.properties文件中添加如下配置来实现。
启动服务后,再次发起地址请求,结果如下:
图片
对于前后端分离开发的情况,这种方式非常实用;但是如果前后端不分离的项目,比如访问项目/static目录下的静态资源,可能前端无法正常访问。
此时,我们可以手动添加资源映射,比如如下操作,前端就能正常访问静态资源了。
2.1.3、自定义异常页面实现
某些场景下,当发生异常时希望跳转到自定义的异常页面,如何实现呢?
首先,这里基于thymeleaf模板引擎来开发页面,在templates目录下创建一个异常页面error.html。
然后,修改异常捕捉方法,这里无需添加@ResponseBody注解,示例如下。
启动服务后,在浏览器中再次请求http://localhost:8080/update,结果如下:
图片
结果与预期一致!
2.1.4、RestControllerAdvice和ControllerAdvice的区别
很多同学喜欢用@RestControllerAdvice来代替@ControllerAdvice和@ResponseBody。
例如如下示例!
实现效果,与上文等价。
打开@RestControllerAdvice的源码,你会发现它将@ControllerAdvice和@ResponseBody注解组合在一起了,因此同时具备两者效果,部分源码如下:
2.2、全局异常处理方式二
在 Spring Boot 中,除了通过@ControllerAdvice和@ExceptionHandler注解实现全局异常处理外,还有一种通过实现HandlerExceptionResolver接口来完成全局异常的处理。
具体实现示例如下:
当出现异常的时候,结果会以json格式响应给客户端。
启动服务后,发起地址请求,结果如下:
这种思路的实现原理,主要是通过 SpringMVC 的异常处理链路器来完成异常的全局处理。
SpringMVC 支持用户自定义异常处理类(需要实现HandlerExceptionResolver),当发生异常时,默认异常处理类无法处理时,就会交给自定义异常处理类来完成。实现方面比较灵活,即可以实现以json格式响应,也可以以页面视图的方式响应。
虽然这种方式能够处理全局异常,但是 Spring 官方不推荐使用它;同时实测过程中发现它无法拦截 404 错误,当请求错误地址时,会优先被DefaultHandlerExceptionResolver默认异常处理类拦截,自定义的异常处理类无法捕捉。
03、小结
最后总结一下,虽然方式一和方式二都可以实现controller层接口请求异常的全局处理,但是在实际使用中,推荐方式一,简单好维护。
示例代码地址: