在Spring Boot中实现全局异常处理,以下是几种常用的方式:
1. @ControllerAdvice + @ExceptionHandler(推荐)
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e) {
log.error("业务异常: {}", e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
// 处理数据校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult()
.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.error(HttpStatus.BAD_REQUEST.value(), message);
}
// 处理参数绑定异常
@ExceptionHandler(BindException.class)
public Result<?> handleBindException(BindException e) {
String message = e.getBindingResult()
.getAllErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.error(HttpStatus.BAD_REQUEST.value(), message);
}
// 处理所有未捕获的异常
@ExceptionHandler(Exception.class)
public Result<?> handleGlobalException(Exception e) {
log.error("系统异常: ", e);
return Result.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统繁忙,请稍后再试");
}
}
2. 自定义异常类
// 基础异常类
public class BaseException extends RuntimeException {
private final Integer code;
public BaseException(Integer code, String message) {
super(message);
this.code = code;
}
public BaseException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public Integer getCode() {
return code;
}
}
// 业务异常
public class BusinessException extends BaseException {
public BusinessException(String message) {
super(HttpStatus.BAD_REQUEST.value(), message);
}
public BusinessException(Integer code, String message) {
super(code, message);
}
}
3. 使用 ErrorController 处理 404 等异常
@RestController
public class CustomErrorController implements ErrorController {
@RequestMapping("/error")
public Result<?> handleError(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
String errorMessage = (String) request.getAttribute("javax.servlet.error.message");
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return Result.error(statusCode, "请求的资源不存在");
}
return Result.error(statusCode, errorMessage != null ? errorMessage : "未知错误");
}
}
4. 统一响应格式
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> implements Serializable {
private Integer code;
private String message;
private T data;
private Long timestamp;
public static <T> Result<T> success() {
return success(null);
}
public static <T> Result<T> success(T data) {
return Result.<T>builder()
.code(HttpStatus.OK.value())
.message("success")
.data(data)
.timestamp(System.currentTimeMillis())
.build();
}
public static <T> Result<T> error(Integer code, String message) {
return Result.<T>builder()
.code(code)
.message(message)
.timestamp(System.currentTimeMillis())
.build();
}
}
5. 配置异常处理(配置文件)
# application.yml
server:
error:
include-exception: false # 不显示异常详情给前端
include-stacktrace: never # 不显示堆栈信息
include-message: always
include-binding-errors: always
spring:
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
6. 完整的全局异常处理器示例
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result<?> businessExceptionHandler(BusinessException e) {
log.error("业务异常: {}", e.getMessage(), e);
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理参数校验异常
*/
@ExceptionHandler(value = {MethodArgumentNotValidException.class,
BindException.class})
public Result<?> validExceptionHandler(Exception e) {
String message = null;
if (e instanceof MethodArgumentNotValidException validException) {
message = validException.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
} else if (e instanceof BindException bindException) {
message = bindException.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
}
log.error("参数校验异常: {}", message);
return Result.error(HttpStatus.BAD_REQUEST.value(), message);
}
/**
* 处理 HTTP 方法不支持异常
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<?> methodNotSupportedExceptionHandler(
HttpRequestMethodNotSupportedException e) {
log.error("HTTP方法不支持: {}", e.getMessage());
return Result.error(HttpStatus.METHOD_NOT_ALLOWED.value(),
"不支持" + e.getMethod() + "请求方法");
}
/**
* 处理媒体类型不支持异常
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public Result<?> mediaTypeNotSupportedExceptionHandler(
HttpMediaTypeNotSupportedException e) {
log.error("媒体类型不支持: {}", e.getMessage());
return Result.error(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(),
"不支持的媒体类型");
}
/**
* 处理文件上传大小限制异常
*/
@ExceptionHandler(MaxUploadSizeExceededException.class)
public Result<?> maxUploadSizeExceededExceptionHandler(
MaxUploadSizeExceededException e) {
log.error("文件大小超出限制: {}", e.getMessage());
return Result.error(HttpStatus.PAYLOAD_TOO_LARGE.value(),
"文件大小超出限制");
}
/**
* 处理重复键异常
*/
@ExceptionHandler(DuplicateKeyException.class)
public Result<?> duplicateKeyExceptionHandler(DuplicateKeyException e) {
log.error("数据重复: {}", e.getMessage());
return Result.error(HttpStatus.BAD_REQUEST.value(),
"数据已存在,请勿重复添加");
}
/**
* 处理乐观锁异常
*/
@ExceptionHandler(OptimisticLockingFailureException.class)
public Result<?> optimisticLockingFailureExceptionHandler(
OptimisticLockingFailureException e) {
log.error("乐观锁异常: {}", e.getMessage());
return Result.error(HttpStatus.CONFLICT.value(),
"数据已被修改,请刷新后重试");
}
/**
* 处理所有未知异常
*/
@ExceptionHandler(Exception.class)
public Result<?> exceptionHandler(Exception e) {
log.error("系统异常: ", e);
return Result.error(HttpStatus.INTERNAL_SERVER_ERROR.value(),
"系统繁忙,请稍后再试");
}
}
7. 使用示例
@RestController
@RequestMapping("/api/user")
public class UserController {
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
User user = userService.getById(id);
if (user == null) {
throw new BusinessException("用户不存在");
}
return Result.success(user);
}
@PostMapping
public Result<String> createUser(@Valid @RequestBody UserDTO userDTO) {
userService.create(userDTO);
return Result.success("创建成功");
}
}
8. 高级配置:自定义异常处理顺序
@Order(Ordered.HIGHEST_PRECEDENCE) // 设置处理顺序
@RestControllerAdvice(assignableTypes = {UserController.class})
public class UserExceptionHandler {
// 处理UserController特定的异常
}
最佳实践建议:
-
分层处理:
-
在Controller层处理参数校验异常
-
在Service层处理业务逻辑异常
-
在全局处理器处理系统级异常
-
-
异常分类:
-
业务异常:返回400-499状态码
-
系统异常:返回500状态码
-
认证授权异常:返回401/403状态码
-
-
日志记录:
-
业务异常记录WARN级别
-
系统异常记录ERROR级别
-
生产环境隐藏敏感信息
-
-
国际化支持:
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e,
Locale locale) {
String message = messageSource.getMessage(e.getMessage(),
null, e.getMessage(), locale);
return Result.error(e.getCode(), message);
}