网站首页 > 技术文章 正文
还在Controller里写满try-catch?还在为杂乱的错误响应格式头疼?是时候用三层统一异常封装终结混乱了!
一、传统异常处理的三大痛点
- 代码重复:每个Controller重复try-catch
- 格式混乱:错误响应五花八门
- 维护困难:异常逻辑散落各处
// 传统写法示例
@PostMapping("/create")
public ResponseEntity<?> createUser(@RequestBody User user) {
try {
userService.save(user);
return ResponseEntity.ok("success");
} catch (DuplicateKeyException e) {
return ResponseEntity.status(500).body("用户已存在"); // 硬编码状态码
} catch (Exception e) {
return ResponseEntity.status(500).body("系统错误"); // 模糊提示
}
}
二、最优方案:三层统一异常封装
1. 统一响应体 (ResultData)
@Data
public class ResultData<T> {
private int status; // 状态码(200/500等)
private String msg; // 提示信息
private T data; // 业务数据
public static <T> ResultData<T> success(T data) {
return new ResultData<>(200, "操作成功", data);
}
public static ResultData<Void> fail(int status, String msg) {
return new ResultData<>(status, msg, null);
}
}
2. 自定义异常体系
// 业务异常(已知错误)
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String msg) {
super(msg);
this.code = code;
}
}
// 系统异常(未知错误)
public class SystemException extends RuntimeException {
public SystemException(String msg) {
super(msg);
}
}
3. 全局异常处理器(核心!)
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理业务异常
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResultData<Void> handleBusinessEx(BusinessException ex) {
return ResultData.fail(ex.getCode(), ex.getMessage());
}
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResultData<Void> handleValidEx(MethodArgumentNotValidException ex) {
String errorMsg = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(";"));
return ResultData.fail(400, errorMsg);
}
// 兜底处理其他异常
@ExceptionHandler(Exception.class)
@ResponseBody
public ResultData<Void> handleOtherEx(Exception ex) {
log.error("系统异常: ", ex);
return ResultData.fail(500, "系统繁忙,请稍后再试");
}
}
三、实战:用户注册场景
1. Service层抛出业务异常
@Service
public class UserService {
public void register(User user) {
if (userRepo.existsByUsername(user.getUsername())) {
throw new BusinessException(1001, "用户名已存在"); // 明确业务错误码
}
try {
userRepo.save(user);
} catch (DataAccessException e) {
throw new SystemException("数据库操作失败"); // 转为系统异常
}
}
}
2. Controller保持简洁
@RestController
public class UserController {
@PostMapping("/register")
public ResultData<String> register(@Validated @RequestBody User user) {
userService.register(user); // 无需try-catch
return ResultData.success("注册成功");
}
}
3. 自动参数校验(配合@Validated)
@Data
public class User {
@NotBlank(message = "用户名不能为空")
private String username;
@Size(min = 6, message = "密码至少6位")
private String password;
}
四、新旧方案对比
对比维度 | 传统方式 | 统一封装方案 |
Controller代码 | 大量try-catch块 | 干净无异常处理代码 |
异常信息格式 | 不统一,随意拼接字符串 | 标准化{status, msg, data} |
错误码管理 | 硬编码在逻辑中 | 集中通过异常类管理 |
参数校验 | 手动校验并返回错误 | 自动拦截并返回标准格式 |
维护成本 | 修改点分散,容易遗漏 | 只需修改全局处理器 |
五、方案优势总结
- 解耦彻底:业务代码与异常处理完全分离
- 响应标准化:前端始终接收统一结构
- 扩展灵活:新增异常只需扩展处理器
- 安全增强:避免向用户暴露堆栈信息
实测数据:某中型项目采用该方案后,Controller代码量减少40%,异常相关Bug下降75%!
关键结论:
- 自定义异常是业务语义的延伸
- @ControllerAdvice是全局处理的基石
- 统一响应体是前后端协作的契约
不要再让异常处理拖垮你的代码!三招组合拳出击:统一格式 + 全局拦截 + 精细分类 = 优雅的异常处理框架。从此和杂乱的try-catch说再见!
猜你喜欢
- 2025-07-28 如何优雅的实现 Spring Boot 接口参数加密解密?
- 2025-07-28 SpringBoot注解最全详解(9大常用注解)
- 2025-07-28 SpringBoot注解全攻略:这些注解让你的代码更专业!
- 2025-07-28 后端使用技术 —— 规范统一入口方法
- 2025-07-28 答应我,不要再用 Map 做出入参了好吗
- 2025-07-28 关于远程调用feign的优雅写法(feign远程调用怎么用restful)
- 2025-07-28 自研分布式高性能RPC框架及服务注册中心ApiRegistry实践笔记
- 2025-07-28 spring基础面试题整理(2)(spring基本面试题)
- 2025-07-28 独立开发:高效集成大模型,看这篇就够了
- 2025-07-28 Spring&SpringBoot常用注解总结
- 1518℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 600℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 521℃MySQL service启动脚本浅析(r12笔记第59天)
- 490℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 489℃启用MySQL查询缓存(mysql8.0查询缓存)
- 477℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 457℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 454℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- windowsscripthost (69)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)