前言
在项目中,接口运行时可能会出现各种异常。比如参数为空、数据不存在、数据库操作失败等。
如果每个 Controller 方法都写 try-catch,代码会变得很乱。所以 Spring Boot 提供了全局异常处理机制,可以统一处理项目中的异常。
这一篇我们来学习 @RestControllerAdvice 和 @ExceptionHandler 的使用。
一、没有全局异常处理的问题
比如新增员工时,如果用户名重复,Service 层可能会抛出异常:
throw new RuntimeException("用户名已存在");
如果没有统一处理,前端可能会收到一大段错误信息,这既不友好,也不安全。
我们希望接口统一返回:
{
"code": 0,
"msg": "用户名已存在",
"data": null
}
这就需要全局异常处理。
二、编写全局异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
e.printStackTrace();
return Result.error("操作失败,请联系管理员");
}
}
这里的 @RestControllerAdvice 表示这是一个全局异常处理类。
@ExceptionHandler(Exception.class) 表示只要项目中出现 Exception 类型异常,就会进入这个方法。
三、处理业务异常
为了让错误提示更明确,可以自定义业务异常。
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
然后在 Service 层抛出:
if (empMapper.getByUsername(emp.getUsername()) != null) {
throw new BusinessException("用户名已存在");
}
全局异常处理:
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException e) {
return Result.error(e.getMessage());
}
这样前端就能收到明确的业务提示。
四、Service 层示例
@Override
public void save(Emp emp) {
Emp oldEmp = empMapper.getByUsername(emp.getUsername());
if (oldEmp != null) {
throw new BusinessException("用户名已存在");
}
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.insert(emp);
}
Service 层只负责判断业务是否成立,如果不成立就抛出异常。
五、Controller 层示例
@PostMapping
public Result save(@RequestBody Emp emp) {
empService.save(emp);
return Result.success();
}
Controller 不需要写 try-catch。
如果 Service 正常执行,就返回成功;如果抛出异常,就交给全局异常处理类统一返回错误结果。
六、常见注解说明
| @RestControllerAdvice | 声明全局异常处理类 |
| @ExceptionHandler | 指定要处理的异常类型 |
| Exception.class | 捕获所有普通异常 |
| BusinessException.class | 捕获自定义业务异常 |
七、实际开发建议
八、总结
全局异常处理可以让项目的错误返回更加统一,也能让 Controller 代码更加干净。
@RestControllerAdvice 负责声明全局异常处理类,@ExceptionHandler 负责指定处理哪一种异常。普通异常可以统一返回通用错误提示,业务异常可以返回更明确的提示信息。
下一篇可以继续写 JWT 登录认证与拦截器实现,这个就更贴近真实项目了。
