美文网首页
[Java] 如果优雅的处理业务异常

[Java] 如果优雅的处理业务异常

作者: zqq90 | 来源:发表于2019-06-10 18:57 被阅读0次

    注: 限于篇幅问题,省略了一些代码实现、文档、一些次要的方法、以及一些衍生方法

    背景

    https://gitee.com/oschina/bullshit-codes/blob/master/java/BadException.java

    • 异常不能复用!异常不能复用!异常不能复用!
    • 其他问题都是毛毛雨啦

    少说废话,看代码

    • 先有个统一的响应体
    @lombok.Data
    @RequiredArgsConstructor(staticName = "of")
    @SuppressWarnings({"WeakerAccess", "unused"})
    public class ResultBody<T> {
    
        @JsonIgnore
        private final int httpStatusHint;
        private final boolean success;
        private final String code;
        private final String message;
        private final T data;
    
        public static <T> ResultBody<T> success() { ... }
    
        public static <T> ResultBody<T> success(T data) { ... }
    
        public static <T> ResultBody<T> failed(String code, String message) { ... }
    
       // 其他的 success(...) failed(...) 略 ...
    
        @JsonIgnore
        public boolean isFailed() {
            return !isSuccess();
        }
    }
    
    • 再有个统一的业务异常类
    @lombok.Getter
    @SuppressWarnings({"WeakerAccess", "unused"})
    public class BusinessException extends RuntimeException {
    
        protected final int statusHint;
        protected final String code;
    
        public BusinessException(String code, String message)  { ... }
    
        public BusinessException(int statusHint, String code, String message) { ... }
    
        public BusinessException(String code, String message, Throwable cause) { ... }
    
        public BusinessException(int statusHint, String code, String message, Throwable cause) {
            //  需要的可以考虑关闭 writableStackTrace 提高性能
            super(message, cause, true, true);
            this.statusHint = statusHint;
            this.code = code;
        }
    
        public <T> ResultBody<T> toResultBody() {
            return ResultBody.failed(statusHint, code, getMessage());
        }
    
       // 其他方法略 ...
    }
    
    • 接下来, 关键点!
    @SuppressWarnings({"unused"})
    public interface IErrors {
    
        // 为啥不是 `getName` 而是 `name` ? 别着急, 往后看
        String name();
    
        int getStatusHint();
    
        default String format(String code, Object[] args) {
            // Tip: 这里还可以处理国际化问题, 喜欢的话, 还可以用其他的占位符语法
            return MessageFormat.format(code, args);
        }
    
        default <T> ResultBody<T> result(String pattern, Object... args) {
            return ResultBody.failed(getStatusHint(), name(), format(pattern, args));
        }
    
        default BusinessException exception(String pattern, Object... args) {
            return new BusinessException(getStatusHint(), name(), format(pattern, args));
        }
    
        default void whenNull(Object obj, String pattern, Object... args) {
            if (obj == null) {
                throw exception(pattern, args);
            }
        }
    
        default void whenFalse(boolean expr, String pattern, Object... args) {
            if (!expr) {
                throw exception(pattern, args);
            }
        }
    
        // 其他 whenXxx(...) 方法略
    }
    
    • 之后,随便定一个枚举,继承 IErrors 就可以了
    @Getter
    @RequiredArgsConstructor
    public enum Errors implements IErrors {
    
        BAD_REQUEST(400),
        UNAUTHORIZED(401),
        NOT_FOUND(404),
        ILLEGAL_ARG(400),
        NOT_SUPPORTED(500),
        SYSTEM(500);
    
        private final int statusHint;
    }
    

    是的,Errors 是个枚举类,我们用枚举的 name 当做 Error Code 再合适不过了

    • 舒服了, 舒服了!
    // 我们可以直接返回 ResultBody<T> 实例
    return Errors.ILLEGAL_ARG
            .result("非法的参数:{0} = '{1}'", "name", name);
    
    // 可以使用异常
    if(bean == null){
        throw Errors.NOT_FOUND
                .exception("您所请求的资源不存在:id={0}", id);
    }
    
    // 还有更舒服的
    Errors.NOT_FOUND
            .whenNull(bean, "您所请求的资源不存在:id={0}", id);
    Errors.BAD_REQUEST
            .whenFalse(bean.isEnable(), "无法完成您的请求,因为该资源已被禁用:id={0}", id);
    
    • 当然,如果就这么完了,你肯定会质疑 IErrors 这个接口的必要性,请看:
    // 像这样,每个模块都可以有自己的 Errors
    @Getter
    @RequiredArgsConstructor
    public enum MyErrors implements IErrors {
    
        // 当然是根据自己的业务来定义啦
        PASSWORD_EXPIRED(400),
        ACCOUNT_DISABLED(400),
        INVALID_TOKEN(401);
    
        private final int statusHint;
    }
    

    是不是很方便?!

    如果有任何意见或建议, 欢迎在评论区留言。”

    相关文章

      网友评论

          本文标题:[Java] 如果优雅的处理业务异常

          本文链接:https://www.haomeiwen.com/subject/bcjtfctx.html