子龙 子龙
首页
学习指南
工具
AI副业
开源项目
技术书籍

程序员子龙

Java 开发从业者
首页
学习指南
工具
AI副业
开源项目
技术书籍
  • 基础

  • JVM

  • Spring

  • 并发编程

  • Mybatis

  • 网络编程

  • 数据库

  • 缓存

  • 设计模式

  • 分布式

  • 高并发

  • SpringBoot

  • SpringCloudAlibaba

  • Nginx

  • 面试

  • 生产问题

  • 系统设计

    • API接口安全设计
    • PO、VO、DAO、BO、DTO、POJO 能分清吗?
    • 别再写满屏的 try catch 了,教你如何统一处理异常!
      • 面试官:生成订单30分钟未支付,则自动取消,该怎么实现?
      • 前后端分离必备的接口规范
      • 如何做到百万数据半小时跑批结束
      • 我总结了写出高质量代码的12条建议
      • 线上的接口响应时间太长,怎么排查?
      • Linux部署Grafana
      • 接口被刷了,怎么办?
      • SpringBoot + vue前后端数据传输加密
      • 面试官:业务开发时,接口不能对外暴露怎么办?有 3 种实现方案!
    • 消息中间件

    • Java
    • 系统设计
    xugaoyi
    2024-01-29
    目录

    别再写满屏的 try catch 了,教你如何统一处理异常!

    在开发过程中,不可避免的是需要处理各种异常,所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块,不仅有大量的冗余代码,而且还影响代码的可读性。

    # 使用统一异常处理

    @ControllerAdvice 处理全局异常,可以指定不同的 ExceptionHandler 处理不同的异常。

    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
    
        /**
         * 业务异常
         * @param request
         * @param response
         * @param e
         * @return
         */
        @ExceptionHandler(ServiceException.class)
        public Object businessExceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e){
            log.error("[GlobalExceptionHandler][businessExceptionHandler] exception",e);
            JsonResult jsonResult = new JsonResult();
            jsonResult.setCode(JsonResultCode.FAILURE);
            jsonResult.setMessage("业务异常,请联系管理员");
            return jsonResult;
        }
    
        /**
         * 全局异常处理
         * @param request
         * @param response
         * @param e
         * @return
         */
        @ExceptionHandler(Exception.class)
        public Object exceptionHandler(HttpServletRequest request,HttpServletResponse response,Exception e)
        {
            log.error("[GlobalExceptionHandler][exceptionHandler] exception",e);
            JsonResult jsonResult = new JsonResult();
            jsonResult.setCode(JsonResultCode.FAILURE);
            jsonResult.setMessage("系统错误,请联系管理员");
            return jsonResult;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

    # 使用 Assert 和Enum抛出异常

        @Test
        public void test1() {
     
            User user = userDao.selectById(userId);
            Assert.notNull(user, "用户不存在");
    
        }
    
        @Test
        public void test2() {
            User user = userDao.selectById(userId);
            if (user == null) {
                throw new userException("用户不存在.");
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    从上面的代码来看,使用 Assert 处理异常要优雅很多,但是这样做有个弊端,要定义很多自定义异常,也很麻烦。

    我们可以使用 Assert 和枚举结合起来处理异常。

    # 自定义断言

    public interface BaseAssert {
        /**
         * 创建异常的接口,具体异常可由实现类来决定
         */
        BaseException newException();
        /**
         * 创建异常的接口,具体异常可由实现类来决定,支持占位符参数列表
         */
        BaseException newException(Object... args);
      
        /**
         * 断言 obj 非空。如果 obj 为空,则抛出异常
         */
        default void isNotNull(Object obj) {
            if (obj == null) {
                throw newException();
            }
        }
        /**
         * 断言 obj 非空。如果 obj 为空,则抛出异常
         * 异常信息 message 支持传递参数方式,避免字符串拼接操作
         */
        default void isNotNull(Object obj, Object... args) {
            if (obj == null) {
                throw newException(args);
            }
        }
        
        //更多类型自行定义
    
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    # 自定义基础异常

    它应该属于运行时异常,并且需要有错误码(code)、错误描述(message)等属性,满足这些条件应该就可以了。

    @Getter
    public class BaseException extends RuntimeException {
        // 错误码
        protected String code;
        // HTTP 状态码
        protected Integer http;
    
        private BaseException(String code, Integer http, String msg) {
            super(msg);
            this.code = code;
            this.http = http;
        }
        private BaseException(String code, Integer http, String msg, Throwable cause) {
            super(msg, cause);
            this.code = code;
            this.http = http;
        }
        public BaseException(ExceptionResponse resp) {
            this(resp.getCode(), resp.getHttp(), resp.getMessage());
        }
        public BaseException(ExceptionResponse resp, String message) {
            this(resp.getCode(), resp.getHttp(), message);
        }
        public BaseException(ExceptionResponse resp, String message, Throwable cause) {
            this(resp.getCode(), resp.getHttp(), message, cause);
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    # 自定义业务异常

    自定义业务异常都应该继承 BaseException 。

    public class BusinessException extends BaseException {
        public BusinessException(ExceptionResponse resp) {
            super(resp);
        }
        public BusinessException(ExceptionResponse resp, String message) {
            super(resp, message);
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    到目前为止已经实现了断言和自定义异常,但是此时我们并没有办法直接使用断言方法。

    # 自定义断言

    public interface BusinessExceptionAssert extends ExceptionResponse, BaseAssert {
        @Override
        default BaseException newException() {
            return new BusinessException(this);
        }
        @Override
        default BaseException newException(Object... args) {
            String msg = MessageFormat.format(this.getMessage(), args);
            return new BusinessException(this, msg);
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 自定义返回实体

    public interface ExceptionResponse {
        /**
         * 获取返回码
         * @return 返回码
         */
        String getCode();
        /**
         * 返回 HTTP 状态码
         * @return
         */
        Integer getHttp();
        /**
         * 获取返回信息
         * @return 返回信息
         */
        String getMessage();
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    # 自定义枚举

    public enum BusinessEnum implements BusinessExceptionAssert {
    
           USER_NOT_EXIST("10001", 400, "用户不存在"),
    
        ;
    
        /**
         * 返回码
         */
        private String code;
        /**
         * 状态码
         */
        private Integer http;
        /**
         * 返回消息
         */
        private String message;
      
        BusinessEnum(String code, Integer http, String message) {
            this.code = code;
            this.http = http;
            this.message = message;
        }
        @Override
        public String getCode() {
            return code;
        }
        @Override
        public String getMessage() {
            return message;
        }
        @Override
        public Integer getHttp() {
            return http;
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

    # 自定义返回实体

    @Data
    public class BaseResponse<T> {
    
    
        private String code;
    
        private String message;
    
        private T data;
    
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 全局异常处理

    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 全局异常处理
         * @param request
         * @param response
         * @param e
         * @return
         */
        @ExceptionHandler(BusinessException.class)
        public Object exceptionHandler(HttpServletRequest request, HttpServletResponse response, BusinessException e) {
            log.error("[GlobalExceptionHandler][exceptionHandler] exception",e);
    
            BaseResponse res = new BaseResponse();
            res.setCode(e.getCode());
            res.setMessage(e.getMessage());
    
            return res;
        }
    
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    # 如何使用

        @RequestMapping(value="/queryById")
        public User queryById(Integer id) {
    
            User user = null;
    
            BusinessEnum.USER_NOT_EXIST.isNotNull(user);
            return user;
        }
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 总结

    使用断言结合枚举的方式,灵活的实现了系统异常情况的判定,既不用定义大量的异常类,让代码可读性更高,以后只需要根据特定的异常情况定义枚举常量就可以了。

    上次更新: 2024/01/30, 15:08:57
    PO、VO、DAO、BO、DTO、POJO 能分清吗?
    面试官:生成订单30分钟未支付,则自动取消,该怎么实现?

    ← PO、VO、DAO、BO、DTO、POJO 能分清吗? 面试官:生成订单30分钟未支付,则自动取消,该怎么实现?→

    最近更新
    01
    保姆级教程 用DeepSeek+飞书,批量写文案、写文章,太高效了
    06-06
    02
    还在为整理视频思维导图发愁?2 种超实用技巧,让你 10 分钟搞定,高效又省心!
    06-06
    03
    熬夜做PPT?AI一键生成高逼格幻灯片,效率提升10倍!
    06-06
    更多文章>
    Theme by Vdoing | Copyright © 2024-2025

        辽ICP备2023001503号-2

    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式