Spring微服务服务层业务结果优雅处理的正确模式咨询
关于异常开销的前提说明
很多文章提到的Java异常性能损耗,主要来源于默认开启的栈跟踪生成逻辑。如果你的自定义业务异常关闭了栈跟踪生成,且业务异常的触发频率没有到每秒数千次的极高并发级别,原有抛出业务异常的方案完全可以长期使用,甚至还可以进一步简化。
方案1:优化现有异常方案(零额外业务代码侵入)
第一步:优化自定义业务异常,关闭栈跟踪
业务异常不需要排查调用栈,直接禁用栈生成即可把异常开销降到和普通对象差不多的水平:
public class CustomBusinessException extends RuntimeException { public CustomBusinessException(String message) { // 构造方法最后两个参数分别为:是否启用抑制、是否生成栈跟踪 super(message, null, false, false); } } // 同理改造UnauthorizedException即可
第二步:添加全局异常处理器,移除所有Controller层的try-catch
用@RestControllerAdvice统一捕获所有业务异常,不需要每个接口单独处理:
@RestControllerAdvice public class GlobalBizExceptionHandler { @ExceptionHandler(UnauthorizedException.class) public ResponseEntity<String> handleUnauthorized(UnauthorizedException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); } @ExceptionHandler(CustomBusinessException.class) public ResponseEntity<String> handleBizError(CustomBusinessException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage()); } }
改造后Controller代码可以直接简化为:
@PostMapping("/checkout") public ResponseEntity<SomeDTO> checkout(@RequestBody CheckoutRequest body) { return ResponseEntity.ok(purchaseService.doCheckout(body)); }
比原有的代码更简洁,完全不需要处理异常分支。
方案2:无异常的结果封装类方案(完全消除异常开销)
如果你不想用异常处理业务分支,尤其是业务违规属于高频触发的正常流程(比如参数校验、库存不足),可以用通用结果封装类实现同样的体验。
第一步:定义通用结果结构
// 业务状态码枚举 public enum BizCode { SUCCESS(200, "成功"), UNAUTHORIZED(401, "未授权"), BIZ_FAIL(400, "业务错误"); public final int code; public final String defaultMsg; BizCode(int code, String defaultMsg) { this.code = code; this.defaultMsg = defaultMsg; } } // 通用返回结果封装 public class BizResult<T> { public int code; public String msg; public T data; public static <T> BizResult<T> success(T data) { BizResult<T> res = new BizResult<>(); res.code = BizCode.SUCCESS.code; res.data = data; return res; } public static <T> BizResult<T> fail(BizCode code, String msg) { BizResult<T> res = new BizResult<>(); res.code = code.code; res.msg = msg; return res; } public boolean isSuccess() { return code == BizCode.SUCCESS.code; } }
第二步:改造Service层返回结果
@Service public class PurchaseService { public BizResult<DTO> doCheckout(CheckoutRequest request) { if (!isUserValid(request.userId)) { return BizResult.fail(BizCode.UNAUTHORIZED, "用户无效"); } if (request.total < 10) { return BizResult.fail(BizCode.BIZ_FAIL, "最低结账金额为20美元"); } // 执行业务逻辑 return BizResult.success(new DTO(someDTOData)); } }
第三步:添加全局响应处理器,自动映射Http状态码
不需要在Controller层写任何if判断,自动根据BizResult的code设置响应状态:
@ControllerAdvice public class BizResultResponseAdvice implements ResponseBodyAdvice<BizResult<?>> { @Override public boolean supports(MethodParameter returnType, Class converterType) { return returnType.getParameterType() == BizResult.class; } @Override public BizResult<?> beforeBodyWrite(BizResult<?> body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (!body.isSuccess()) { response.setStatusCode(HttpStatus.valueOf(body.code)); } return body; } }
改造后Controller代码如下:
@PostMapping("/checkout") public BizResult<SomeDTO> checkout(@RequestBody CheckoutRequest body) { return purchaseService.doCheckout(body); }
开发体验和异常方案完全一致,没有任何异常开销。
选型建议
- 业务违规属于低频的异常场景:选优化后的异常方案,代码侵入性更低、更简洁
- 业务违规属于高频的正常业务分支:选结果封装类方案,性能更优
内容的提问来源于stack exchange,提问作者Dio Deiva
相关产品推荐
相关产品推荐

