主流两种方案:@RestControllerAdvice + @ExceptionHandler (推荐),分统一返回结果、自定义业务异常、全局异常捕获、分层拦截完整实现。
一、前置:统一响应实体
项目接口统一返回格式,便于前端处理。
java
import lombok.Data;
/**
* 全局统一返回结果
*/
@Data
public class Result<T> {
// 响应码:200成功,500系统异常,自定义业务码自行定义
private Integer code;
// 响应信息
private String message;
// 响应数据
private T data;
// 成功响应
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("操作成功");
result.setData(data);
return result;
}
public static <T> Result<T> success() {
return success(null);
}
// 失败响应
public static <T> Result<T> fail(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
result.setData(null);
return result;
}
public static <T> Result<T> fail(String message) {
return fail(500, message);
}
}
二、自定义业务异常
区分系统异常 和业务异常,精准捕获业务报错。
java
/**
* 自定义业务异常
*/
public class BusinessException extends RuntimeException {
private Integer code;
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(String message) {
super(message);
this.code = 500;
}
public Integer getCode() {
return code;
}
}
三、全局异常处理器(核心)
使用 @RestControllerAdvice 作用于所有 Controller ,配合 @ExceptionHandler 拦截指定异常。
java
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
* RestControllerAdvice = ControllerAdvice + ResponseBody
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
// 1. 捕获自定义业务异常(优先匹配)
@ExceptionHandler(BusinessException.class)
public Result<?> businessExceptionHandler(BusinessException e) {
return Result.fail(e.getCode(), e.getMessage());
}
// 2. 捕获参数校验异常(如 @Valid 校验失败)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> validExceptionHandler(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldError().getDefaultMessage();
return Result.fail(400, "参数错误:" + msg);
}
// 3. 捕获空指针异常
@ExceptionHandler(NullPointerException.class)
public Result<?> nullPointerExceptionHandler() {
return Result.fail(500, "系统空指针异常,请联系管理员");
}
// 4. 兜底:捕获所有未知异常(最后执行)
@ExceptionHandler(Exception.class)
public Result<?> exceptionHandler(Exception e) {
// 生产环境可打印日志 e.printStackTrace()
return Result.fail(500, "系统繁忙,请稍后重试");
}
}
注解说明
@RestControllerAdvice- 全局切面,拦截所有
@RestController/@Controller接口 - 自带
@ResponseBody,返回 JSON 格式
- 全局切面,拦截所有
@ExceptionHandler(异常类.class)- 指定要拦截的异常类型,精准异常优先于父类异常
四、使用测试
1. 编写测试 Controller
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
// 测试业务异常
@GetMapping("/test/biz")
public Result<?> testBiz() {
throw new BusinessException(1001, "用户名不存在");
}
// 测试空指针
@GetMapping("/test/null")
public Result<?> testNull() {
String str = null;
str.length();
return Result.success();
}
// 测试普通异常
@GetMapping("/test/error")
public Result<?> testError() {
int a = 1 / 0;
return Result.success();
}
}
2. 调用接口效果
/test/biz→ 返回code:1001, message:用户名不存在/test/null→ 返回空指针提示/test/error→ 被Exception兜底拦截
五、扩展常用场景
1. 拦截 SpringMVC 404/405 等状态异常
需在 application.yml 开启异常转发:
yaml
spring:
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
新增异常处理方法:
java
// 拦截404找不到接口异常
@ExceptionHandler(NoHandlerFoundException.class)
public Result<?> noHandlerFoundException() {
return Result.fail(404, "请求接口不存在");
}
2. 统一日志打印(生产必备)
异常处理器中加入日志,方便排查问题:
java
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result<?> exceptionHandler(Exception e) {
log.error("系统未知异常:", e);
return Result.fail(500, "系统繁忙,请稍后重试");
}
}
六、关键点总结
- 异常优先级 :具体异常 > 自定义异常 >
Exception兜底 - 适用范围 :
@RestControllerAdvice只拦截Controller 层异常,Service/工具类主动抛异常依然会被捕获 - 规范建议 :业务场景统一抛
BusinessException,系统异常由全局处理器统一封装 - 排除拦截 :如需部分 Controller 不生效,可配合
@ControllerAdvice(basePackages = "com.xxx.controller")指定包范围