优雅的抛异常方式: 断言 + 枚举类 + 自定义异常
传统的抛异常方式:
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);
}
}
修改前后对比:
修改过后,抛异常通过断言+枚举的形式,增强抛异常拓展性,枚举类则可以定制更多的异常信息,如code,message等
如有问题,请咨询:QQxin7045