Springboot ControllerAdvice轻松搞定全局异常处理

    xiaoxiao2023-11-12  141

    本文在 【从零到一 Springboot+Mybatis_Plus示例】 的基础之上,提出问题分析改进。

    文章目录

    1 、 问题分析2、 异常处理流程3、可预知异常处理3.1 自定义异常类3.2 自定义抛出异常类3.3 自定义异常捕获类3.4 错误码NewsCode3.5修改业务代码 测试异常 4、不可预知异常处理4.1 不可知异常实例4.2 异常捕获方法4.3 测试

    1 、 问题分析

    首先看一段上文的服务是实现类(NewsServiceImpl)的示例代码:

    /** * 添加新闻 * * @param title * @param summary * @return */ public boolean insertNews(String title, String summary) { if (title == null || title.length() < 1) { return false; } News record = new News(); record.setTitle(title); record.setSummary(summary); record.setCreateTime(LocalDateTime.now()); ; return super.save(record); }

    我们简单分析下上面的代码问题:

    上边的代码返回结果只知道添加失败还是成功,如果失败了无法得知具体的错误信息。service方法在执行过程出现异常在哪捕获?在service中需要都加try/catch,如果在controller也需要添加try/catch,代码冗余严重且不易维护。 基于以上问题提出解决方案:在Service方法中的编码顺序是先校验判断,有问题则抛出具体的异常信息,最后执行具体的业务操作,返回成功信息。在统一异常处理类中去捕获异常,无需controller捕获异常,向用户返回统一规范的响应信息。定义统一返回结果实体等 例如下面这样: /** * 添加新闻 * * @param title * @param summary * @return */ public InsertResult insertNews(String title, String summary) { if (title == null || title.length() < 1) { //抛出自定义异常 } News record = new News(); record.setTitle(title); record.setSummary(summary); record.setCreateTime(LocalDateTime.now()); ; return new InsertResult(.....); }

    2、 异常处理流程

    系统对异常的处理使用统一的异常处理流程:

    自定义异常类型。自定义错误代码及错误信息。对于可预知的异常由程序员在代码中主动抛出,由SpringMVC统一捕获。 可预知异常是程序员在代码中手动抛出本系统定义的特定异常类型,由于是程序员抛出的异常,通常异常信息比较齐全,程序员在抛出时会指定错误代码及错误信息,获取异常信息也比较方便。对于不可预知的异常(运行时异常)由SpringMVC统一捕获Exception类型的异常。不可预知异常通常是由于系统出现bug、或一些不要抗拒的错误(比如网络中断、服务器宕机等),异常类型为RuntimeException类型(运行时异常)。可预知的异常及不可预知的运行时异常最终会采用统一的信息格式(错误代码+错误信息)来表示,最终也会随请求响应给客户端。 异常抛出及处理流程: 在controller、service、dao中程序员抛出自定义异常;springMVC框架抛出框架异常类型统一由异常捕获类捕获异常,并进行处理捕获到自定义异常则直接取出错误代码及错误信息,响应给用户。捕获到非自定义异常类型首先从Map中找该异常类型是否对应具体的错误代码,如果有则取出错误代码和错误信息并响应给用户,如果从Map中找不到异常类型所对应的错误代码则统一为99999错误代码并响应给用户。将错误代码及错误信息以Json格式响应给用户。

    3、可预知异常处理

    新建exception包,定义异常类型。

    3.1 自定义异常类
    package com.qqxhb.mybatis.exception; import com.qqxhb.mybatis.model.response.ResultCode; public class CustomException extends RuntimeException { /** * */ private static final long serialVersionUID = 1L; // 错误代码 ResultCode resultCode; public CustomException(ResultCode resultCode) { this.resultCode = resultCode; } public ResultCode getResultCode() { return resultCode; } }
    3.2 自定义抛出异常类
    package com.qqxhb.mybatis.exception; import com.qqxhb.mybatis.model.response.ResultCode; public class ExceptionCast { public static void cast(ResultCode resultCode) { throw new CustomException(resultCode); } }
    3.3 自定义异常捕获类
    package com.qqxhb.mybatis.exception; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import com.qqxhb.mybatis.model.response.ResponseResult; import com.qqxhb.mybatis.model.response.ResultCode; /** * 统一异常捕获类 * **/ @ControllerAdvice // 控制器增强 public class ExceptionCatch { private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionCatch.class); // 捕获CustomException此类异常 @ExceptionHandler(CustomException.class) @ResponseBody public ResponseResult customException(CustomException customException) { // 记录日志 LOGGER.error("catch exception:{}", customException.getMessage()); ResultCode resultCode = customException.getResultCode(); return new ResponseResult(resultCode); } }
    3.4 错误码NewsCode
    package com.qqxhb.mybatis.model.response; import lombok.ToString; @ToString public enum NewsCode implements ResultCode { REQUIRED_PARAMISNULL(false, 3000, "必填参数为空!"); // 操作代码 boolean success; // 操作代码 int code; // 提示信息 String message; private NewsCode(boolean success, int code, String message) { this.success = success; this.code = code; this.message = message; } @Override public boolean success() { return success; } @Override public int code() { return code; } @Override public String message() { return message; } }
    3.5修改业务代码 测试异常
    public boolean insertNews(String title, String summary) { if (title == null || title.length() < 1) { ExceptionCast.cast(NewsCode.REQUIRED_PARAMISNULL); return false; } News record = new News(); record.setTitle(title); record.setSummary(summary); record.setCreateTime(LocalDateTime.now()); return super.save(record); }

    4、不可预知异常处理

    4.1 不可知异常实例

    比如发其一个不存在的请求,这样的响应信息在客户端是无法解析。

    4.2 异常捕获方法

    针对上边的问题其解决方案是:

    我们在map中配置HttpRequestMethodNotSupportedException的错误为非法请求(CommonCode.INVALID_REQUEST)。在异常捕获类中对Exception异常进行捕获,并从map中获取异常类型对应的错误代码,如果存在错误代码则返回此错误,否则统一返回99999错误。 修改异常捕获类: package com.qqxhb.mybatis.exception; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import com.google.common.collect.ImmutableMap; import com.qqxhb.mybatis.model.response.CommonCode; import com.qqxhb.mybatis.model.response.ResponseResult; import com.qqxhb.mybatis.model.response.ResultCode; /** * 统一异常捕获类 * **/ @ControllerAdvice // 控制器增强 public class ExceptionCatch { private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionCatch.class); // 定义map,配置异常类型所对应的错误代码 private static ImmutableMap<Class<? extends Throwable>, ResultCode> EXCEPTIONS; // 定义map的builder对象,去构建ImmutableMap protected static ImmutableMap.Builder<Class<? extends Throwable>, ResultCode> builder = ImmutableMap.builder(); // 捕获CustomException此类异常 @ExceptionHandler(CustomException.class) @ResponseBody public ResponseResult customException(CustomException customException) { // 记录日志 LOGGER.error("catch exception:{}", customException.getMessage()); ResultCode resultCode = customException.getResultCode(); return new ResponseResult(resultCode); } // 捕获Exception此类异常 @ExceptionHandler(Exception.class) @ResponseBody public ResponseResult exception(Exception exception) { // 记录日志 LOGGER.error("catch exception:{}", exception.getMessage()); if (EXCEPTIONS == null) { EXCEPTIONS = builder.build();// EXCEPTIONS构建成功 } // 从EXCEPTIONS中找异常类型所对应的错误代码,如果找到了将错误代码响应给用户,如果找不到给用户响应99999异常 ResultCode resultCode = EXCEPTIONS.get(exception.getClass()); if (resultCode != null) { return new ResponseResult(resultCode); } else { // 返回99999异常 return new ResponseResult(CommonCode.SERVER_ERROR); } } static { // 定义异常类型所对应的错误代码 builder.put(HttpRequestMethodNotSupportedException.class, CommonCode.INVALID_REQUEST); } }
    4.3 测试

    未设置HttpRequestMethodNotSupportedException的错误码时: 添加错误码之后测试: 源码地址:https://github.com/qqxhb/springboot-mybatis-demo 项目名称:springboot-handle-exception

    最新回复(0)