文章目录

  • 概述
  • 未使用全局异常且未显式捕获异常的情况
  • 使用全局异常
    • Step1. 自定义异常类
    • Step2. 封装异常信息模板
    • Step3. 全局异常处理类
    • Step4. 使用全局异常
  • 小结

概述

我们在Spring Boot2.x-07Spring Boot2.1.2整合Mybatis这边文章的基础上来实现下Spring Boot使用@ControllerAdvice和@ExceptionHandler实现自定义全局异常。

首先需要明确的是:@ControllerAdvice 顾名思义主要处理的就是 controller 层的异常信息,没有进入 controller 层的异常@ControllerAdvice 是无法处理的。 如果需要处理这种错误可以继承BasicErrorController,可参考 https://segmentfault.com/a/1190000008443705

现在前后端分离的趋势,前端通过ajax调用Restful接口,约定前后端的接口规范,后台只需要按照约定格式返回JSON给前端即可,越来越少的项目会在Controller层糅合ModelAndView的信息了。

假定我们这里的项目是前后端分离,我们来探讨下基于此种场景的全局异常处理(因此全局异常处理类我们使用了@RestControllerAdvice

为什么需要全局异常呢?

  • Spring Boot 会将所有的异常发送到路径为server.error.path(application.properties中可以配置,默认为”/error”)的控制器方法中进行处理,详见BasicErrorController源码 ,提示不友好

  • 如果未使用全局异常的情况下,大量使用try-catch,难以阅读,有些时候因为异常被try-catch捕获导致@Transactional注解失效

比如我们之前写的o2o的项目

Controller层充满了大量的try-catch【不推荐使用try-catch,增大了代码量,当异常过多对应的catch也就越多,不方便维护和扩展】,而且也只是简单粗暴的返回Map<String, Object>,通过@ResponseBody转换为JSON返回给前台,非常不优雅。

约定好返回格式+使用全局异常后,Controller层就清爽了很多,无需try-catch,并且还能避免因为异常被try-catch捕获导致@Transactional注解失效。


未使用全局异常且未显式捕获异常的情况

我们先看下如果没有全局异常,并且也没有对异常进行捕获,直接使用Spring Boot默认的异常显示会怎样呢?

先把个字段名故意写错来看下,

Controller层的代码如下:

启动Spring Boot工程,访问下Controller层暴露的接口
http://localhost:8080/artisans

经典的Whitelabel Error Page

如上图,可以看到是非常的不友好,那这里我们来使用全局异常来改造下吧。


使用全局异常


Step1. 自定义异常类

项目中

package com.artisan.exception;import lombok.Getter;/*** 需要继承RuntimeException。* 另外Spring 对于 RuntimeException类型的 异常才会进行事务回滚* @author yangshangwei**/
public class MyCustomException extends RuntimeException {private static final long serialVersionUID = 8863339790253662109L;@Getterprivate int code ;@Getterprivate String message;public MyCustomException() {super();}public MyCustomException(int code , String message) {this.message = message;this.code = code;}}

Step2. 封装异常信息模板

统一返回的异常信息的格式

package com.artisan.exception;import lombok.Getter;/*** 统一返回的异常信息的格式* * @author yangshangwei**/public class ExceptionResponseEntity {@Getterprivate int code;@Getterprivate String message;public ExceptionResponseEntity() {}public ExceptionResponseEntity(int code, String message) {this.code = code;this.message = message;}}

Step3. 全局异常处理类

说明见代码注释

package com.artisan.exception;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;/*** *  @ControllerAdvice 捕获 Controller 层抛出的异常,如果添加 @ResponseBody 返回信息则为JSON 格式。@RestControllerAdvice 相当于 @ControllerAdvice 与 @ResponseBody 的结合体。@ExceptionHandler 统一处理一种类的异常,减少代码重复率,降低复杂度。因为我们这里全部异常信息都约定返回json,所以直接使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody了步骤:1.创建一个 GlobalExceptionHandler 类,并添加上 @RestControllerAdvice 注解就可以实现异常通知类的定义了2.定义的方法中添加上 @ExceptionHandler 即可实现Controller层的异常捕捉**/@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler{/*** 如果需要捕获多个异常   定义如下:@ExceptionHandler({})* * @param request* @param e* @param response* @return*/// 捕获多个异常的写法@ExceptionHandler({MyCustomException.class,MyCustomException.class})public ExceptionResponseEntity customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());MyCustomException exception = (MyCustomException) e;logger.info("MyCustomException");return new ExceptionResponseEntity(exception.getCode(), exception.getMessage());}/***  捕获  RuntimeException 异常*  如果在一个 exceptionHandler 通过  if (e instanceof xxxException) 太麻烦,*  可以写多个方法标注@ExceptionHandler处理不同的异常** @param request  request* @param e        exception* @param response response* @return 响应结果*/@ExceptionHandler(RuntimeException.class)public ExceptionResponseEntity runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());RuntimeException exception = (RuntimeException) e;logger.info("RuntimeException");return new ExceptionResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), exception.getMessage());}}

通过 @ControllerAdvice(或者@RestControllerAdvice) 和 @ExceptionHandler 实现了对全局异常的捕获。

这里仅定义了2个异常,一个是自定义的MyCustomException,另外一个是RuntimeException,按需增加自定义的异常类即可。


Step4. 使用全局异常

因为我们把数据库字段写错了,所以这个方法肯定是抛出异常的,假定dao层和service层都未对异常进行处理,那么Controller层抛出的异常返回给前端是什么样的呢?

启动Spring Boot工程,
http://localhost:8080/artisans

结合控制台输出的日志

可知,GlobalExceptionHandler#runtimeExceptionHandler捕获了该异常,而不是我们文章开始的那个经典的Whitelabel Error Page页面了


那我们刚才自定义的那个异常怎么来捕获呢? 其实很简单,只要throw即可

为了演示用法,我们修改下Controller层的方法 如下

访问 http://localhost:8080/artisans

结合后台日志可知 GlobalExceptionHandler#customExceptionHandler捕获了该异常


小结

这里是使用@ControllerAdvice和@ExceptionHandler来实现全局的异常处理,其他方式比如使用AOP的方式也是可行的。 还有一种基于Spring Boot本身的全局异常统一处理,主要是实现ErrorController接口或者继承AbstractErrorController抽象类或者继承BasicErrorController类。

具体可参考这位大神的博客
https://blog.csdn.net/hao_kkkkk/article/details/80538955

Spring Boot2.x-11 使用@ControllerAdvice和@ExceptionHandler实现自定义全局异常相关推荐

  1. Spring boot异常统一处理方法:@ControllerAdvice注解的使用、全局异常捕获、自定义异常捕获

    Spring boot异常统一处理方法:@ControllerAdvice注解的使用.全局异常捕获.自定义异常捕获 参考文章: (1)Spring boot异常统一处理方法:@ControllerAd ...

  2. Spring MVC异常处理 - @ ControllerAdvice,@ ExceptionHandler,HandlerExceptionResolver

    Spring MVC异常处理 - @ ControllerAdvice,@ ExceptionHandler,HandlerExceptionResolver Spring MVC异常处理对于确保您不 ...

  3. spring mvc统一异常处理(@ControllerAdvice + @ExceptionHandler)

    spring mvc统一异常处理(@ControllerAdvice + @ExceptionHandler) 参考文章: (1)spring mvc统一异常处理(@ControllerAdvice ...

  4. Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json

    需求 在构建RestFul的今天,我们一般会限定好返回数据的格式比如: { "code": 0,   "data": {},   "msg" ...

  5. 微服务架构之全局异常(@ControllerAdvice + @ExceptionHandler)

    微服务架构之全局异常处理 一.定义全局异常处理类(GlobalExceptionHandler) 1.在cloud-common模块中创建全局异常处理类GlobalExceptionHandler 2 ...

  6. Spring Boot2.x-09 基于Spring Boot 2.1.2 + Mybatis使用自定义注解实现数据库切换

    文章目录 概述 场景说明:读写分离 操作步骤 工程结构 Step1 自定义注解 Step2 数据源定义 Step3 配置文件配置数据源 Step4 数据源实例化DatasourceConfig Ste ...

  7. SpringBoot使用ControllerAdvice和ExceptionHandler进行统一异常处理

    @ControllerAdvice和@RestControllerAdvice(两者区别和@Controller/@RestController类似 , 都只是响应内容上的区别)是Spring提供的一 ...

  8. Spring Boot2.1.5(2)---2.x 新特性

    Spring Boot 官网在 2019/05/15 这天发布了 Spring Boot 2.1.5 正式版. Spring Boot 2.1.5 新特性: 1.增加了 Spring Session ...

  9. spring boot2.0整合富文本编辑器summernote

    summernote对上传图片,以及对图片大小细节,删除图片移除服务器资源等处理的比较完美. 整合过程,summernote的官网https://summernote.org/getting-star ...

最新文章

  1. jdbc桥连接过程解析
  2. How to configure cross-stack EtherChannel on Cisco Catalyst 3750 switches
  3. 【集训队作业2018】复读机【指数型生成函数】【单位根反演】【二项式定理】
  4. Packet Tracer 通过配置静态路由实现不同网段之间的通信(详细步骤)
  5. C++: error: call of overloaded ‘abs(int)’ is ambiguous
  6. UPESB天气查询用例(三)
  7. CCF NOI1098 森林
  8. 怎么不能锁门_镜子能不能对着床
  9. HDU 1097 JAVA
  10. 如何快速填充表格公式
  11. 公务员面试综合分析真题解析
  12. html背景渐变蓝色,CSS3网页渐变色背景,适用于IE
  13. 机器人学笔记之——操作臂运动学:驱动器空间、关节空间和笛卡尔空间
  14. 激励人生成功的10句经典英文
  15. MVT 之 M——模型
  16. 【综合类型第 28 篇】ReSharper 的安装、使用教程
  17. java设计模式——浅显易懂之七大原则
  18. js混淆 反混淆 在线
  19. 用友数据库类型 mysql_用友软件用的是什么数据库?
  20. 关于光猫连接无线路由设置问题

热门文章

  1. rdesktop 登录腾讯云
  2. amh支持java吗_AMH 6.0 发布,国内领先的云主机面板
  3. java todo error_java基础-异常
  4. python3 tensorflowprint错误_解决import tensorflow as tf 出错的原因
  5. 文巾解题 面试题 01.02. 判定是否互为字符重排
  6. MATLAB应用实战系列( 七十五) -图像处理应用 MATLAB实现基于分水岭算法的图像分割 (附matlab代码)
  7. 完美解答35K月薪的MySQL面试题(四)MySQL是如何加行锁的?
  8. Tableau必知必会之连接shapefile空间文件进行地图分析
  9. shell 循环判断语法
  10. Matlab读取avi视频并播放