使用异常枚举类让if( condition ){ throw Exception }变的简洁点吧

现在大多数的Web接口项目,对于一些业务异常处理,都是直接以Exception 的方式向外面抛出,再定义一个全局异常捕获器(ControllerAdvice) 进行统一的处理

背景

先来看一段非空判断抛出异常的代码

// 查询用户id是否有效
	User user = userService.findUserById(1);
	if(user==null){
		throw new GenericBusinessException(ResponseErrorEnum.USER_NOT_EXIST);
	}
	//如果用户存在,尝试修改token...
	// 其它有关用户操作,在此省略....

相信在项目中,这种类似判断的代码有很多,思考以下,如果很多地方都要判断用户id是否有效,那这段代码应该要写很多遍吧.

为了做一名积极向上的搬运工,我觉得还是要想办法解决下这个问题.

知识点提前知晓

接下去的实现方法里,涉及到:

  • JDK8 引入的interface 里可以编写default 默认方法 .emm ,要知道以前接口里是没有方法体的 , 每个接口实现类都必须要重写接口定义的全部方法,即使,有些接口你都没想法去实现,这样,你也还是要写个空方法体哇哇哇.
  • 由Spring出品的spring-core里的Assert 类,直接看notNull的源码吧,很简单
    public static void notNull(@Nullable Object object, String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }

断言: 可以看作是异常处理的一种高级形式。

解决办法

核心就是参考Assert ,让异常枚举类也有断言的功能.

先看下最后效果:

	//ResponseEnum.CAN_NOT_IS_NULL : 枚举类
   @Test
    public void testAssert() throws Exception {
        User user = null;
        ResponseEnum.CAN_NOT_IS_NULL.assertNotNull(user);
    }    

看下控制台的日志:
在这里插入图片描述
开始写代码

  • (1) Assert 基类 : 规定一些基本特性
public interface MyAssert {
    //具体抛出什么类型的异常,由子类重写
    Exception newException(Object... args);
    //共通判断-对象空指针
    default void assertNotNull(Object obj) throws Exception {
        if (obj == null) {
            throw newException(obj);
        }
    }
    //共通判断-集合不为空
    default void assertNotListEmpty(List<?> list) throws Exception {
        if (list == null || list.isEmpty()) {
            throw newException(list);
        }
    }
}
  • (2) 枚举接口 : 为后续的 业务Assert 类服务 . 提供错误信息 (code + message)
public interface IResponseEnum {
    int getCode(); // 异常code
    String getMessage();//异常message
}
  • (3) 业务Assert 类 : 处理一些非共通的异常 , 并 自定义Exception
public interface BusinessExceptionAssert extends IResponseEnum, MyAssert {
    //jdk1.8特性:
    // (1)实现类不需要自己再重写default方法,会自动继承该方法 。按照1.8之前的,接口里的每个方法,实现类都是需要重写的。
    //  (2)同时带来的多继承中的一个二义性问题:如果一个类实现了两个接口(可以看做是“多继承”),这两个接口又同时都包含了一个名字相同的default方法,那么会发生什么情况? 在这样的情况下,编译器会报错。
    default GeneraBusinessException newException(Object... args) {
        //getMessage() 调用的是 IResponseEnum
        String msg = MessageFormat.format(this.getMessage(), args);
        return new GeneraBusinessException(this, args, msg);
    }

    //这里可以进行拓展, 更具体化的业务异常判断
    default void assertUserIsValid(User user) throws GeneraBusinessException {
        if (user.getAge() <= 0 || StringUtils.isEmpty(user.getUsername())) {
            throw newException(user.getAge());
        }
    }

    //每个assert方法都是对应每个ResponseEnum 的逻辑判断,通过这种方式,主要是为了减少业务代码里的很多if(和枚举相关的判断)
    //所以,枚举类型越多,这里的assert方法也就越多
}
  • (4) 异常枚举类 : 最亮的点 ,继承了BusinessExceptionAssert 接口,却只要实现IResponseEnum的两个方法, 因为其它方法,接口已提供了default 方法. 强大的JDK8 .
@Getter
@AllArgsConstructor
public enum ResponseEnum implements BusinessExceptionAssert {
    //{x}占位符
    CAN_NOT_IS_NULL(1001, "不能为空"),
    ;
    /**
     * 异常码
     */
    private int code;
    /**
     * 异常消息
     */
    private String message;
}
  • (5) 单元测试
   @Test
    public void testAssert() throws Exception {
        User user = null;
        ResponseEnum.CAN_NOT_IS_NULL.assertNotNull(user);
    }  

思考优缺点

优点:
(1) 高频率出现的判断逻辑都放到Assert 里 , 降低了和其它业务逻辑的耦合性. 假如判断逻辑突然发生了改变, 这样 只需要修改 Assert 里的判断即可 . 按照最初的 if 写法, 可能修改代码的地方不止1处啊啊啊啊啊;
(2) 代码看起来更简洁, 毕竟少写了几行 或者 几十行的 if (…){…} , 心情应该不是特别差吧;

缺点:
(1) emm…我觉得 可能就是可读性不是特别好, 大多数的人第一次看到这种写法应该想骂人吧~

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值