异常处理最佳实践总结
2025/11/30大约 3 分钟
🌐 总结
📌 1. 为什么需要全局异常处理
在 Web 开发中,如果不处理异常:
- 后端会直接抛 500
- 前端无法拿到统一的错误格式
- message 不规范、格式不一致
- 日志无法统一记录
解决方案:使用自定义异常 + 全局异常处理器(@RestControllerAdvice)统一响应格式。
📌 2. 统一响应结构(R)
统一定一个返回模板,让前后端都用同一种 JSON 格式:
@Data
public class R<T> {
private Integer code;
private String message;
private T data;
public static <T> R<T> success(T data) {
R<T> r = new R<>();
r.code = 200;
r.message = "success";
r.data = data;
return r;
}
public static <T> R<T> error(Integer code, String message) {
R<T> r = new R<>();
r.code = code;
r.message = message;
return r;
}
}前端收到的数据格式:
成功:
{
"code": 200,
"message": "success",
"data": { ... }
}异常:
{
"code": 401,
"message": "密码错误",
"data": null
}📌 3. 自定义业务异常(BusinessException)
业务错误(如密码错误、余额不足)不应该抛系统异常,应由业务异常统一描述。
public class BusinessException extends RuntimeException {
private Integer code;
public BusinessException(Integer code, String message) {
super(message); // 把 message 写入 Throwable.detailMessage
this.code = code; // 自定义字段
}
public Integer getCode() {
return code;
}
}📌 4. 为什么 BusinessException 必须 super(message)
原因:
- Java 异常体系只认识 父类 Throwable 的 message 字段
- 所有日志/框架都调用
ex.getMessage() - 若不 super(message),
getMessage()得到的是 null
对比:
super(message); //✔ message 写入异常体系(Throwable.detailMessage)
this.code = code; //✔ code 仅业务使用📌 5. 全局异常处理器(核心)
捕获所有异常 → 包装成统一响应结构 → 返回给前端。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public R<?> handleBusinessException(BusinessException ex) {
return R.error(ex.getCode(), ex.getMessage());
}
@ExceptionHandler(Exception.class)
public R<?> handleException(Exception ex) {
ex.printStackTrace();
return R.error(500, "服务器内部错误");
}
}📌 6. Spring 捕获异常的匹配机制
Spring 按以下顺序捕获异常:
- 最具体的子类优先(如 BusinessException)
- 找不到则匹配父类(如 RuntimeException)
- 最后匹配 Exception.class(兜底)
因此:
- 抛 BusinessException → handleBusinessException()
- 其他异常 → handleException()
📌 7. 异常传播流程(非常重要)
Service 层抛出 BusinessException
↓
Controller 不处理 → 自动向外抛
↓
Spring MVC 捕获异常
↓
匹配 @ExceptionHandler(BusinessException.class)
↓
执行 GlobalExceptionHandler.handleBusinessException()
↓
返回统一 JSON 响应📌 8. 示例:业务中抛出异常
if (user == null) {
throw new BusinessException(401, "用户不存在");
}
if (!user.getPassword().equals(encryptPassword)) {
throw new BusinessException(401, "密码错误");
}前端收到:
{
"code": 401,
"message": "密码错误",
"data": null
}📌 9. 统一返回格式带来的好处
✔ 前端只需要根据 code 判断成功/失败
✔ 后端异常信息标准化
✔ 系统错误与业务错误分离
✔ 全链路日志规范
✔ 便于后续扩展(错误码枚举、国际化等)
📌 10. 最佳实践结构图
Controller → Service → (throw BusinessException)
↓
GlobalExceptionHandler
↓
R.error(code, message)
↓
JSON 返回📌 11. 最终总结(面试可背)
在 Spring Web 中,通过自定义 BusinessException 抛出业务异常,
使用 @RestControllerAdvice + @ExceptionHandler 捕获异常,
并将其统一封装为R<T>结构返回前端。
super(message) 用于将错误信息写入 Throwable.message,使整个 Java 异常体系可读取。
子类字段(如 code)用于业务逻辑扩展,不需写入父类。
如果你愿意,我还能帮你写:
📌 错误码枚举(更专业)
📌 Restful API 的完整结构
📌 Axios 前端统一错误拦截器
你想要哪部分?
