方案 1:Service 返回 Result,而不是抛业务异常
JDK 21 很适合用 sealed interface 表达结果类型:
public sealed interface Result<T>
permits Result.Success, Result.NotFound, Result.BadRequest, Result.Failed {
record Success<T>(T data) implements Result<T> {
}
record NotFound<T>(String msg) implements Result<T> {
}
record BadRequest<T>(String msg) implements Result<T> {
}
record Failed<T>(String msg) implements Result<T> {
}
}
public class Response<T> {
private final int code;
private final String msg;
private final T data;
public Response(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Response<T> from(Result<T> result) {
return switch (result) {
case Result.Success<T> r ->
new Response<>(200, "success", r.data());
case Result.NotFound<T> r ->
new Response<>(404, r.msg(), null);
case Result.BadRequest<T> r ->
new Response<>(400, r.msg(), null);
case Result.Failed<T> r ->
new Response<>(500, r.msg(), null);
};
}
}
public Result<Void> doService() {
var entity = repository.findById(id);
if (entity.isEmpty()) {
return new Result.NotFound<>("resource not found");
}
// do something
return new Result.Success<>(null);
}
public Response<Void> bar() {
return Response.from(service.doService());
}
方案 2:保留异常,但在边界内吞掉“已知业务异常”
如果你们现在 service 已经大量抛 NotFoundException、BizException,短期可以先做一个 wrapper,但只处理业务异常:
@FunctionalInterface
public interface ThrowingSupplier<T> {
T get() throws Exception;
}
public class Response<T> {
private String msg;
private int code;
private T data;
public Response(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> Response<T> ok(ThrowingSupplier<T> supplier) {
try {
T data = supplier.get();
return new Response<>(200, "success", data);
} catch (Exception e) {
return handleException(e);
}
}
private static <T> Response<T> handleException(Exception e) {
return switch (e) {
case NotFoundException ex ->
new Response<>(404, ex.getMessage(), null);
case IllegalArgumentException ex ->
new Response<>(400, ex.getMessage(), null);
case NullPointerException ex ->
new Response<>(500, "Null pointer error", null);
default ->
new Response<>(500, "Internal server error", null);
};
}
}
controller:
public Response<Void> bar() {
return Response.safe(() -> {
service.doService();
});
}