首页 > 其他分享 >SpringBoot中@ControllerAdvice/@RestControlAdvice+@ExceptionHandler实现全局异常捕获与处理

SpringBoot中@ControllerAdvice/@RestControlAdvice+@ExceptionHandler实现全局异常捕获与处理

时间:2023-05-11 12:23:07浏览次数:54  
标签:AjaxResult ControllerAdvice SpringBoot RestControlAdvice final static msg public

场景

在编写Controller接口时,为避免接口因为未知的异常导致返回不友好的结果和提示。

如果不进行全局异常捕获则需要对每个接口进行try-catch或其他操作。

 

可以对Controller进行全局的异常捕获和处理,一旦发生异常,则返回通用的500响应码与通用错误提示。

并将异常发生的具体的文件、类、方法、行数信息记录到日志。

@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,

可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理。

需要配合@ExceptionHandler使用。当将异常抛到controller时,可以对异常进行统一处理。

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

1、@RestControlAdvice是组合注解,由@ControllerAdvice和@ResponseBody组成。

 

@ControllerAdvice 提供了多种指定Advice规则的定义方式,默认什么都不写,则是Advice所有Controller,

当然你也可以通过下列的方式指定规则。

通过basePackages指定只对哪些包路径下生效。

也可以通过指定注解来匹配,比如我自定了一个 @CustomAnnotation 注解,

我想匹配所有被这个注解修饰的 Controller, 可以这么写:@ControllerAdvice(annotations= {CustomAnnotation.class})

 

这里不做任何规则限制,对所有的controller生效。

2、新建全局异常捕获类,注意与启动类在同包路径下,不然不生效需要在启动类中指定包路径。

import com.badao.demo.common.AjaxResult;
import com.badao.demo.constant.Constants;
import com.badao.demo.constant.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//注意引入包的路径
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e)
    {
        StackTraceElement stackTrace = e.getStackTrace()[0];
        String methodName = stackTrace.getMethodName();
        String fileName = stackTrace.getFileName();
        String className = stackTrace.getClassName();
        int lineNumber = stackTrace.getLineNumber();
        log.error("异常发生在文件{}的类{}中的方法{}的第{}行',异常信息:{}", fileName,className,methodName,lineNumber,e.getMessage());
        return AjaxResult.error(HttpStatus.ERROR, Constants.SERVER_ERROR);
    }
}

这里使用@ExceptionHandler(Exception.class)

对所有的Exception进行捕获,然后统一返回封装的AjaxResult结果类

import com.badao.demo.constant.HttpStatus;
import com.badao.demo.utils.StringUtils;

import java.util.HashMap;

/**
 * 操作消息提醒
 *
 */
public class AjaxResult extends HashMap<String, Object>
{
    private static final long serialVersionUID = 1L;

    /** 状态码 */
    public static final String CODE_TAG = "code";

    /** 返回内容 */
    public static final String MSG_TAG = "msg";

    /** 数据对象 */
    public static final String DATA_TAG = "data";

    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult()
    {
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg 返回内容
     */
    public AjaxResult(int code, String msg)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg 返回内容
     * @param data 数据对象
     */
    public AjaxResult(int code, String msg, Object data)
    {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success()
    {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @return
     */
    public static AjaxResult error()
    {
        return AjaxResult.error("操作失败");
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult error(int code, String msg)
    {
        return new AjaxResult(code, msg, null);
    }
}

然后状态码和提示消息是封装的常量类

状态码常量类

public class HttpStatus
{
    /**
     * 操作成功
     */
    public static final int SUCCESS = 200;

    /**
     * 对象创建成功
     */
    public static final int CREATED = 201;

    /**
     * 请求已经被接受
     */
    public static final int ACCEPTED = 202;

    /**
     * 操作已经执行成功,但是没有返回数据
     */
    public static final int NO_CONTENT = 204;

    /**
     * 资源已被移除
     */
    public static final int MOVED_PERM = 301;

    /**
     * 重定向
     */
    public static final int SEE_OTHER = 303;

    /**
     * 资源没有被修改
     */
    public static final int NOT_MODIFIED = 304;

    /**
     * 参数列表错误(缺少,格式不匹配)
     */
    public static final int BAD_REQUEST = 400;

    /**
     * 未授权
     */
    public static final int UNAUTHORIZED = 401;

    /**
     * 访问受限,授权过期
     */
    public static final int FORBIDDEN = 403;

    /**
     * 资源,服务未找到
     */
    public static final int NOT_FOUND = 404;

    /**
     * 不允许的http方法
     */
    public static final int BAD_METHOD = 405;

    /**
     * 资源冲突,或者资源被锁
     */
    public static final int CONFLICT = 409;

    /**
     * 不支持的数据,媒体类型
     */
    public static final int UNSUPPORTED_TYPE = 415;

    /**
     * 系统内部错误
     */
    public static final int ERROR = 500;

    /**
     * 接口未实现
     */
    public static final int NOT_IMPLEMENTED = 501;
}

消息常量类

public class Constants {
    //请求响应常量
    public static final String NO_API_CODE = "请求码不能为空";
    public static final String ILLEGAL_API_CODE = "请求码不存在";
    public static final String SERVER_ERROR = "服务器内部错误,请联系管理员!";
    public static final String CALL_TOO_OFEN = "请求太频繁";
}

然后将异常发生的具体信息记录到日志中,异常的信息来源可以打断点获取和自定义需要获取的内容

 

2、这里是对Exception类型进行通配,如果需要对指定的异常类型或者自定义类型就行捕获,还可以

import com.badao.demo.common.AjaxResult;
import com.badao.demo.constant.Constants;
import com.badao.demo.constant.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//注意引入包的路径
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e)
    {
        StackTraceElement stackTrace = e.getStackTrace()[0];
        String methodName = stackTrace.getMethodName();
        String fileName = stackTrace.getFileName();
        String className = stackTrace.getClassName();
        int lineNumber = stackTrace.getLineNumber();
        log.error("异常发生在文件{}的类{}中的方法{}的第{}行',异常信息:{}", fileName,className,methodName,lineNumber,e.getMessage());
        return AjaxResult.error(HttpStatus.ERROR, Constants.SERVER_ERROR);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.error(HttpStatus.BAD_REQUEST, message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(message);
    }
}

这里注意引入包的路径,不然会出现不生效的情况。

这块还可参考

SpringBoot+@Validate+全局异常拦截实现自定义规则参数校验(校验get请求参数不能为空且在指定枚举类型中):

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/130605437

3、测试效果

手动编写一个除零异常并将controller中的try-catch去掉

 

接口统一响应,在日志中记录到具体信息

 

标签:AjaxResult,ControllerAdvice,SpringBoot,RestControlAdvice,final,static,msg,public
From: https://www.cnblogs.com/badaoliumangqizhi/p/17390693.html

相关文章

  • springboot跨域问题解决方案
    以下内容仅供自己学习使用,侵权私聊必删。在进行前后端交互的时候,往往会遇到以下的跨域问题。那么解决这种跨域的话,可以使用以下这种方法:(引自于程序员青戈)创建config配置目录新建CorsConfig类然后把下面的内容复制进去根据自己需要修改以下就可以解决跨域问题啦importo......
  • SpringBoot整合规则引擎Drools
    目录1整合规则引擎Drools1.1前言1.2pom.xml1.3Drools配置类1.4示例Demo1.4.1添加业务Model1.4.2定义drools规则1.4.3添加Service层1.4.4添加Controller1.4.5测试1.5drools规则解析1.5.1简介1.5.2规则体语法结构1.5.3注释1.5.4Pattern模式匹配1.5.5比较操作符1.5.......
  • springboot集成springSwagger生成接口文档
    1.首先引入pom.xml依赖<!--SwaggerAPI文档--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version><exclusions><exclus......
  • java基于springboot+vue的房屋租赁租房系统、租房管理系统,附源码+数据库,免费包运行,适
    1、项目介绍java基于springboot+vue的房屋租赁租房系统、租房管理系统,分为管理员和用户。用户的功能有:登录、注册、房屋信息、交流论坛、房屋咨询、在线客服、个人中心、我的收藏、我的发布、预约看房管理、在线签约管理、租赁评价管理、管理员的功能有:登录、个人中心、用户管......
  • SpringBoot上传图片到resource下
    推荐博客:https://blog.csdn.net/weixin_52065369/article/details/120412307这样上传到resource下的图片需要重启编译后才能访问,需要配置以下才能访问的到,通常不采用这样的方式https://blog.csdn.net/qq_41604890/article/details/114553632上传图片到本机......
  • springboot自动装配过程
    一、首先要知道springboot的启动类然后知道启动类有一个重要的注解:@SpringBootApplication然后跟踪查看,它是由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan注解组成的@SpringBootConfiguration作用是声明当前类是一个组件@ComponentScan作用是扫描启......
  • springboot+Prometheus+grafana 实现自定义监控(请求数、响应时间、JVM性能)
    自定义监控1.SpringBoot工程集成Micrometer1.1引入依赖1.2配置1.3监控jvm信息1.4创建自定义监控1.5添加具体业务代码监控2.集成Prometheus2.1安装2.2集成配置3.使用GrafanaDashboard展示监控项3.1安装grafana3.2配置prometheus数据源3.3增加jvm面板3.4配置业务接口监控面板......
  • 【SpringBoot】【自动装配】 SpringBoot自动装配原理
    1 前言我们都知道SpringBoot有个自动装配的机制,那你们知道平时如何使用么,以及他是什么时候执行的么,那么本节我们就来看看。2  为什么要有自动装配我们经历过SSM时期的时候,比如要引进Redis,是不是要先要导Maven依赖,还要进行大量的配置Bean,然后才能使用,而使用SpringBoot的......
  • springboot 项目中返回前端对象错误显示是string格式
    原因是返回json对象后面跟了一段,如下图这个错误藏的比较隐蔽,有个小的对象没有实现getter方法。在返回前端对象里,所有对象都得可以序列化和反序列化,对应的对象中所有属性是否都实现getter和setter等序列化。......
  • SpringBoot 配置文件加载优先级
    我们在使用springboot开发的时候,经常会从外部获取属性值,为了记住这些规则,特此做如下记录~~~一、为什么要做外部化配置本地开发的时候,上传文件的时候,每个人想上传的路径不一样,使用外部配置,就可以单独设置自己的上传路径项目部署的时候,不同的环境使用不同的配置,使用外部挂载配置这......