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

简介: Spring Boot2.x-11 使用@ControllerAdvice和@ExceptionHandler实现自定义全局异常

概述


我们在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的项目


20190212171400763.png



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


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


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


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

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

20190212175216341.png


Controller层的代码如下:


20190212215548106.png


启动Spring Boot工程,访问下Controller层暴露的接口

http://localhost:8080/artisans

经典的Whitelabel Error Page


20190212175135937.png



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


使用全局异常

2019021221340237.png


Step1. 自定义异常类

项目中

package com.artisan.exception;
import lombok.Getter;
/**
 * 需要继承RuntimeException。
 * 另外Spring 对于 RuntimeException类型的 异常才会进行事务回滚
 * @author yangshangwei
 *
 */
public class MyCustomException extends RuntimeException {
  private static final long serialVersionUID = 8863339790253662109L;
  @Getter
  private int code ;
  @Getter
  private 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 {
  @Getter
  private int code;
  @Getter
  private 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层抛出的异常返回给前端是什么样的呢?


20190212214651571.png



启动Spring Boot工程,

http://localhost:8080/artisans

20190212215831623.png


结合控制台输出的日志

20190212215947269.png

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


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


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

20190212220258254.png


访问 http://localhost:8080/artisans

20190212220325431.png



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


20190212220412794.png


小结


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


具体可参考这位大神的博客

https://blog.csdn.net/hao_kkkkk/article/details/80538955

相关文章
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
14878 60
|
8月前
|
监控 安全 Java
使用 @HealthEndpoint 在 Spring Boot 中实现自定义健康检查
Spring Boot 通过 Actuator 模块提供了强大的健康检查功能,帮助开发者快速了解应用程序的运行状态。默认健康检查可检测数据库连接、依赖服务、资源可用性等,但在实际应用中,业务需求和依赖关系各不相同,因此需要实现自定义健康检查来更精确地监控关键组件。本文介绍了如何使用 @HealthEndpoint 注解及实现 HealthIndicator 接口来扩展 Spring Boot 的健康检查功能,从而提升系统的可观测性与稳定性。
593 0
使用 @HealthEndpoint 在 Spring Boot 中实现自定义健康检查
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——处理系统异常
本文介绍了在Spring Boot项目中如何通过创建`GlobalExceptionHandler`类来全局处理系统异常。通过使用`@ControllerAdvice`注解,可以拦截项目中的各种异常,并结合`@ExceptionHandler`注解针对特定异常(如参数缺失、空指针等)进行定制化处理。文中详细展示了处理参数缺失异常和空指针异常的示例代码,并说明了通过拦截`Exception`父类实现统一异常处理的方法。虽然拦截`Exception`可一劳永逸,但为便于问题排查,建议优先处理常见异常,最后再兜底处理未知异常,确保返回给调用方的信息友好且明确。
1493 0
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——处理系统异常
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
1042 1
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——拦截自定义异常
本文介绍了在实际项目中如何拦截自定义异常。首先,通过定义异常信息枚举类 `BusinessMsgEnum`,统一管理业务异常的代码和消息。接着,创建自定义业务异常类 `BusinessErrorException`,并在其构造方法中传入枚举类以实现异常信息的封装。最后,利用 `GlobalExceptionHandler` 拦截并处理自定义异常,返回标准的 JSON 响应格式。文章还提供了示例代码和测试方法,展示了全局异常处理在 Spring Boot 项目中的应用价值。
610 0
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
2416 15
|
Dubbo Java 应用服务中间件
深入探讨了“dubbo+nacos+springboot3的native打包成功后运行出现异常”的原因及解决方案
本文深入探讨了“dubbo+nacos+springboot3的native打包成功后运行出现异常”的原因及解决方案。通过检查GraalVM版本兼容性、配置反射列表、使用代理类、检查配置文件、禁用不支持的功能、查看日志文件、使用GraalVM诊断工具和调整GraalVM配置等步骤,帮助开发者快速定位并解决问题,确保服务的正常运行。
722 1
|
Java API Spring
springBoot:注解&封装类&异常类&登录实现类 (八)
本文介绍了Spring Boot项目中的一些关键代码片段,包括使用`@PathVariable`绑定路径参数、创建封装类Result和异常处理类GlobalException、定义常量接口Constants、自定义异常ServiceException以及实现用户登录功能。通过这些代码,展示了如何构建RESTful API,处理请求参数,统一返回结果格式,以及全局异常处理等核心功能。
233 1
|
安全 Java 应用服务中间件
如何将Spring Boot应用程序运行到自定义端口
如何将Spring Boot应用程序运行到自定义端口
1578 0