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

2019-06-10  本文已影响0人  zqq90

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

背景

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(...) 方法略
}
@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);
// 像这样,每个模块都可以有自己的 Errors
@Getter
@RequiredArgsConstructor
public enum MyErrors implements IErrors {

    // 当然是根据自己的业务来定义啦
    PASSWORD_EXPIRED(400),
    ACCOUNT_DISABLED(400),
    INVALID_TOKEN(401);

    private final int statusHint;
}

是不是很方便?!

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

上一篇 下一篇

猜你喜欢

热点阅读