优雅的抛异常方式 [断言 + 枚举类 + 自定义异常]

100 阅读7分钟

优雅的抛异常方式: 断言 + 枚举类 + 自定义异常

传统的抛异常方式:

if (object == null) {
    throw new BaseException("没有找到女朋友异常!");
}

优雅的抛异常方式:

-   AssertExceptionEnum.NOT_FIND_GRIL_FRIEND_EXCEPTION.predicate(object, ObjectUtil::isNull, "没有找到女朋友异常!");

代码结构:

  • base
    • AssertX
    • BaseException
    • ExceptionResult
  • asserts
    • AssertExceptionAssert
  • enums
    • AssertExceptionEnum
  • exceptions
    • AssertException
  • handler
    • ExceptionHandler
  • util
    • FormatUtil
  • vo
    • Result

废话不多说,上代码:

AssertX:

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.lang.NonNull;

import java.util.Collection;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 接口:断言接口方法定义
 *
 * @author a-xin
 * @date 2024/1/19 13:59
 */
public interface AssertX {

    /**
     * 创建异常的接口,具体异常可由实现类来决定
     */
    BaseException newException();

    /**
     * 创建异常的接口,具体异常可由实现类来决定,支持占位符参数列表
     */
    BaseException newException(String pattern, Object... message);

    /**
     * 断言对象是否为空
     *
     * @param bool 需要判断的对象
     */
    default void exceptionIf(@NonNull Boolean bool) {
        if (bool) {
            throw newException();
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param bool    需要判断的对象
     * @param message 抛出的异常信息
     */
    default void exceptionIf(@NonNull Boolean bool, String pattern, Object... message) {
        if (bool) {
            throw newException(pattern, message);
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param bool     需要判断的对象
     * @param runnable 需要执行的流程
     */
    default void exceptionIf(@NonNull Boolean bool, @NonNull Runnable runnable) {
        if (bool) {
            throw newException();
        } else {
            runnable.run();
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param bool    需要判断的对象
     * @param message 抛出的异常信息
     */
    default void exceptionIf(@NonNull Boolean bool, @NonNull Runnable runnable, String pattern, Object... message) {
        if (bool) {
            throw newException(pattern, message);
        } else {
            runnable.run();
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param bool     需要判断的对象
     * @param runnable 需要执行的流程
     */
    default <R> R exceptionIf(@NonNull Boolean bool, @NonNull Supplier<R> runnable) {
        if (bool) {
            throw newException();
        } else {
            return runnable.get();
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param bool    需要判断的对象
     * @param message 抛出的异常信息
     */
    default <R> R exceptionIf(@NonNull Boolean bool, @NonNull Supplier<R> runnable, String pattern, Object... message) {
        if (bool) {
            throw newException(pattern, message);
        } else {
            return runnable.get();
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param obj 需要判断的对象
     */
    default void isNull(Object obj) {
        if (ObjectUtil.isNull(obj)) {
            throw newException();
        }
    }

    /**
     * 断言对象是否为空
     *
     * @param obj     需要判断的对象
     * @param message 抛出的异常信息
     */
    default void isNull(Object obj, String pattern, Object... message) {
        if (ObjectUtil.isNull(obj)) {
            throw newException(pattern, message);
        }
    }

    /**
     * 断言字符串是否为空
     *
     * @param str 需要判断的字符串
     */
    default void isBlank(String str) {
        if (CharSequenceUtil.isBlank(str)) {
            throw newException();
        }
    }

    /**
     * 断言字符串是否为空
     *
     * @param str     需要判断的字符串
     * @param message 抛出的异常信息
     */
    default void isBlank(String str, String pattern, Object... message) {
        if (CharSequenceUtil.isBlank(str)) {
            throw newException(pattern, message);
        }
    }

    /**
     * 断言集合是否为空
     *
     * @param collection 需要判断的集合
     */
    default <T> void isEmpty(Collection<T> collection) {
        if (CollUtil.isEmpty(collection)) {
            throw newException();
        }
    }

    /**
     * 断言集合是否为空
     *
     * @param collection 需要判断的集合
     * @param message    抛出的异常信息
     */
    default <T> void isEmpty(Collection<T> collection, String pattern, Object... message) {
        if (CollUtil.isEmpty(collection)) {
            throw newException(pattern, message);
        }
    }

    /**
     * 根据断言判断是否抛出异常
     *
     * @param parameter 断言参数
     * @param predicate 断言方法
     * @param <P>       断言参数类型
     */
    default <P> void predicate(P parameter, @NonNull Predicate<P> predicate) {
        if (predicate.test(parameter)) {
            throw newException();
        }
    }

    /**
     * 根据断言判断是否抛出异常
     *
     * @param parameter 断言参数
     * @param predicate 断言方法
     * @param message   抛出的异常信息
     * @param <P>       断言参数类型
     */
    default <P> void predicate(P parameter, @NonNull Predicate<P> predicate, String pattern, Object... message) {
        if (predicate.test(parameter)) {
            throw newException(pattern, message);
        }
    }

    /**
     * 根据断言判断是否抛出异常
     *
     * @param parameter 断言参数
     * @param predicate 断言方法
     * @param runnable  断言后需要执行的操作,再抛出异常
     * @param <P>       断言参数类型
     */
    default <P> void predicate(P parameter, @NonNull Predicate<P> predicate, @NonNull Runnable runnable) {
        if (predicate.test(parameter)) {
            runnable.run();
            throw newException();
        }
    }

    /**
     * 根据断言判断是否抛出异常
     *
     * @param parameter 断言参数
     * @param predicate 断言方法
     * @param runnable  断言后需要执行的操作,再抛出异常
     * @param message   执行操作后抛出的异常信息
     * @param <P>       断言参数类型
     */
    default <P> void predicate(P parameter, @NonNull Predicate<P> predicate, @NonNull Runnable runnable, String pattern, Object... message) {
        if (predicate.test(parameter)) {
            runnable.run();
            throw newException(pattern, message);
        }
    }

    /**
     * 根据断言判断是否抛出异常,有返回值
     *
     * @param parameter 断言参数
     * @param predicate 断言方法
     * @param runnable  断言后需要执行的操作,再抛出异常
     * @param <P>       断言参数类型
     */
    default <P, R> R predicate(P parameter, @NonNull Predicate<P> predicate, @NonNull Supplier<R> runnable) {
        if (predicate.test(parameter)) {
            return runnable.get();
        } else {
            throw newException();
        }
    }

    /**
     * 根据断言判断是否抛出异常,有返回值
     *
     * @param parameter 断言参数
     * @param predicate 断言方法
     * @param runnable  断言后需要执行的操作,再抛出异常
     * @param <P>       断言参数类型
     */
    default <P, R> R predicate(P parameter, @NonNull Predicate<P> predicate, @NonNull Supplier<R> runnable, String pattern, Object... message) {
        if (predicate.test(parameter)) {
            return runnable.get();
        } else {
            throw newException(pattern, message);
        }
    }

}

BaseException:

import lombok.Getter;

/**
 * 接口:异常基础类,已定义,不修改
 *
 * @author a-xin
 * @date 2023/1/19 2:16 PM
 */
@Getter
public class BaseException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private final String message;
    private Integer code = 500;

    public BaseException(String message) {
        super(message);
        this.message = message;
    }

    public BaseException(String message, Throwable e) {
        super(message, e);
        this.message = message;
    }

    public BaseException(String message, Integer code) {
        super(message);
        this.message = message;
        this.code = code;
    }

}

ExceptionResult:

/**
 * 类名:异常类枚举定义返回数据方法
 *
 * @author a-xin
 * @date 2024/1/19 14:03
 */
public interface ExceptionResult {

    /**
     * 获取返回码
     *
     * @return 返回码
     */
    Integer getCode();

    /**
     * 获取返回信息
     *
     * @return 返回信息
     */
    String getMessage();

}

AssertExceptionAssert:

import com.axin229913.exception.base.AssertX;
import com.axin229913.exception.base.ExceptionResult;
import com.axin229913.exception.exceptions.AssertException;
import com.axin229913.constant.util.FormatUtil;

/**
 * 类名:定义异常断言接口信息
 *
 * @author a-xin
 * @date 2024/1/19 14:02
 */
public interface AssertExceptionAssert extends ExceptionResult, AssertX {
    @Override
    default AssertException newException() {
        return new AssertException(this.getMessage());
    }

    @Override
    default AssertException newException(String pattern, Object... message) {
        return new AssertException(FormatUtil.formatLogStr(pattern, message), this.getCode());
    }
}

AssertExceptionEnum:

import com.axin229913.exception.asserts.AssertExceptionAssert;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Objects;

/**
 * 异常类型枚举类
 *
 * @author a-xin
 * @date 2023/1/19 2:14 PM
 */
@Getter
@AllArgsConstructor
public enum AssertExceptionEnum implements AssertExceptionAssert {

    NORMAL_ASSERT_EXCEPTION(502, "Assertion anomalies!"),
    //就像当前的你没有找到女朋友一样的异常~
    NOT_FIND_GRIL_FRIEND_EXCEPTION(1314, "NOT_FIND_GRIL_FRIEND_EXCEPTION!"),
    ;

    private final String message;
    private final Integer code;

    AssertExceptionEnum(int code, String message) {
        this.message = message;
        this.code = code;
    }

    public static String getMessage(Integer code) {
        for (AssertExceptionEnum c : AssertExceptionEnum.values()) {
            if (Objects.equals(c.getCode(), code)) {
                return c.message;
            }
        }
        return null;
    }

    public String getMessage() {
        return message;
    }

    public Integer getCode() {
        return code;
    }

}

AssertException:

import com.axin229913.exception.base.BaseException;
import com.axin229913.exception.enums.AssertExceptionEnum;
import com.axin229913.constant.util.FormatUtil;
import lombok.Getter;

/**
 * 接口:权限校验公共异常类
 *
 * @author a-xin
 * @date 2023/1/19 2:16 PM
 */
@Getter
public class AssertException extends BaseException {

    private static final long serialVersionUID = 1L;

    public AssertException(String pattern, Object... message) {
        super(FormatUtil.formatLogStr(pattern, message));
    }

    public AssertException(String message, Throwable e) {
        super(message, e);
    }

    public AssertException(Throwable e, String pattern, Object... message) {
        super(FormatUtil.formatLogStr(pattern, message), e);
    }

    public AssertException(int code, String pattern, Object... message) {
        super(FormatUtil.formatLogStr(pattern, message), code);
    }

    public AssertException(AssertExceptionEnum exceptionTypeEnum) {
        super(exceptionTypeEnum.getMessage(), exceptionTypeEnum.getCode());
    }

    public AssertException(AssertExceptionEnum exceptionTypeEnum, String pattern, Object... message) {
        super(FormatUtil.formatLogStr(pattern, message), exceptionTypeEnum.getCode());
    }

}

ExceptionHandler:

import com.axin229913.constant.vo.Result;
import com.axin229913.exception.exceptions.AssertException;
import com.axin229913.exception.exceptions.CommonException;
import com.axin229913.exception.exceptions.FeignException;
import com.axin229913.exception.exceptions.NotFindGirlFriendException;
import com.axin229913.exception.prometheus.PrometheusCustomMonitor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.annotation.Resource;

/**
 * 类名:全局异常捕获
 *
 * @author a-xin
 * @date 2:29 PM
 */
@ControllerAdvice
public class ExceptionHandler {

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(RuntimeException.class)
    public Result<String> exceptionResult(RuntimeException e) {
        e.printStackTrace();
        return Result.fail(e.getMessage(), e.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @org.springframework.web.bind.annotation.ExceptionHandler(value = AssertException.class)
    private Result<String> customExceptionHandler(AssertException e) {
        e.printStackTrace();
        return Result.fail(e.getCode(), e.getMessage());
    }

}

FormatUtil:

import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 类名:格式化工具类
 *
 * @author a-xin
 * @date 2024/1/22 15:30
 */
@Slf4j
@Component
public class FormatUtil {

    /**
     * 日志类型打印格式化字符串
     *
     * @param pattern 需要替换的字符串信息,{}为替换符号
     * @param args    需要替换的参数信息
     * @return 替换完成的字符串信息
     */
    public static String formatLogStr(String pattern, Object... args) {
        if (ObjectUtil.isNull(args)) {
            return pattern;
        }
        AtomicReference<String> result = new AtomicReference<>(pattern);
        Arrays.stream(args).forEach(arg -> result.set(result.get().replaceFirst("\\{}", arg.toString())));
        return result.get();
    }

    /**
     * 格式化字符串
     *
     * @param sign    需要替换的字符串,需要转义
     * @param pattern 需要替换的字符串信息
     * @param args    需要替换的参数信息
     * @return 替换完成的字符串信息
     */
    public static String formatStr(String sign, String pattern, Object... args) {
        if (ObjectUtil.isNull(args)) {
            return pattern;
        }
        AtomicReference<String> result = new AtomicReference<>(pattern);
        Arrays.stream(args).forEach(arg -> result.set(result.get().replaceFirst(sign, arg.toString())));
        return result.get();
    }

}

Result:

import cn.hutool.core.util.RandomUtil;
import com.axin229913.constant.enums.ExceptionTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 类名:统一返回数据类型
 *
 * @author a-xin
 * @date 2022/8/25
 */
@Data
@Accessors(chain = true)
@SuppressWarnings("all")
public class Result<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 响应状态码
     */
    private Integer code;

    /**
     * 响应信息
     */
    private String message;

    /**
     * 响应状态码
     */
    private String status = RandomUtil.randomString(16);

    /**
     * 响应数据
     */
    private T data;

    /**
     * 成功响应信息
     */
    private static final String SUCCESS_INFO = "Operation successful!";
    private static final Integer SUCCESS_CODE = 200;

    /**
     * 失败相应信息
     */
    private static final String FAIL_INFO = "Operation is abnormal!";
    private static final Integer FAIL_CODE = 500;

    /**
     * 成功响应
     *
     * @return 无数据
     */
    public static Result<String> success() {
        return new Result().setCode(SUCCESS_CODE)
                .setMessage(SUCCESS_INFO)
                .setData(SUCCESS_INFO);
    }

    /**
     * 成功响应带数据
     *
     * @param data 数据信息
     * @param <T>  数据类型
     * @return 返回值
     */
    public static <T> Result<T> success(T data) {
        return new Result().setCode(SUCCESS_CODE)
                .setMessage(SUCCESS_INFO)
                .setData(data);
    }

    /**
     * 失败响应
     *
     * @return 返回值
     */
    public static Result<String> fail() {
        return new Result().setCode(FAIL_CODE)
                .setMessage(FAIL_INFO)
                .setData(FAIL_INFO);
    }

    /**
     * 失败响应
     *
     * @param data 数据
     * @param <T>  数据类型
     * @return 返回值
     */
    public static <T> Result<T> fail(T data) {
        return new Result().setCode(FAIL_CODE)
                .setMessage(FAIL_INFO)
                .setData(data);
    }

    /**
     * 失败响应
     *
     * @param message 失败信息
     * @param data    数据
     * @param <T>     数据类型
     * @return 返回值
     */
    public static <T> Result<T> fail(String message, T data) {
        return new Result().setCode(FAIL_CODE)
                .setMessage(message)
                .setData(data);
    }

    /**
     * 失败响应
     *
     * @param code    失败代码
     * @param message 失败信息
     * @param data    数据
     * @param <T>     数据类型
     * @return 返回值
     */
    public static <T> Result<T> fail(Integer code, String message, T data) {
        return new Result().setCode(code)
                .setMessage(message)
                .setData(data);
    }

    /**
     * 失败响应
     *
     * @param code    失败代码
     * @param message 失败信息
     * @return 返回值
     */
    public static <T> Result<T> fail(Integer code, String message) {
        return new Result().setCode(code)
                .setMessage(message);
    }

    /**
     * 失败响应
     *
     * @param data              数据
     * @param exceptionTypeEnum 异常类型
     * @param <T>               数据类型
     * @return 返回值
     */
    public static <T> Result<T> fail(ExceptionTypeEnum exceptionTypeEnum, T data) {
        return new Result().setCode(exceptionTypeEnum.getCode())
                .setMessage(exceptionTypeEnum.getMessage())
                .setData(data);
    }

}

修改前后对比:

image.png

修改过后,抛异常通过断言+枚举的形式,增强抛异常拓展性,枚举类则可以定制更多的异常信息,如code,message等

如有问题,请咨询:QQxin7045