本文目录导读:

我来为您提供一个完整的Spring Boot全局异常处理案例,包含常见的异常处理场景。
项目依赖配置
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
统一响应结果封装
// Result.java
import lombok.Data;
import java.io.Serializable;
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String message;
private T data;
private long timestamp = System.currentTimeMillis();
public Result() {}
public Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> success(String message, T data) {
return new Result<>(200, message, data);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
public static <T> Result<T> error(Integer code, String message, T data) {
return new Result<>(code, message, data);
}
}
// ResultCode.java 状态码枚举
public enum ResultCode {
SUCCESS(200, "操作成功"),
BAD_REQUEST(400, "请求参数错误"),
UNAUTHORIZED(401, "未认证"),
FORBIDDEN(403, "无权限"),
NOT_FOUND(404, "资源不存在"),
INTERNAL_SERVER_ERROR(500, "系统内部错误"),
SERVICE_UNAVAILABLE(503, "服务不可用");
private final Integer code;
private final String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
自定义业务异常
// BusinessException.java
import lombok.Getter;
@Getter
public class BusinessException extends RuntimeException {
private final Integer code;
private final String message;
public BusinessException(String message) {
super(message);
this.code = ResultCode.INTERNAL_SERVER_ERROR.getCode();
this.message = message;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public BusinessException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public BusinessException(ResultCode resultCode, String message) {
super(message);
this.code = resultCode.getCode();
this.message = message;
}
}
全局异常处理器(核心类)
// GlobalExceptionHandler.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.stream.Collectors;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Result<Object>> handleBusinessException(BusinessException e, WebRequest request) {
log.error("业务异常: {}", e.getMessage());
log.error("请求路径: {}", request.getDescription(false));
return ResponseEntity
.status(HttpStatus.OK)
.body(Result.error(e.getCode(), e.getMessage()));
}
/**
* 处理参数校验异常(@RequestBody)
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Result<Object>> handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
String message = e.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
log.warn("参数校验失败: {}", message);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(Result.error(ResultCode.BAD_REQUEST.getCode(), message));
}
/**
* 处理参数绑定异常(表单提交)
*/
@ExceptionHandler(BindException.class)
public ResponseEntity<Result<Object>> handleBindException(BindException e) {
String message = e.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
log.warn("参数绑定失败: {}", message);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(Result.error(ResultCode.BAD_REQUEST.getCode(), message));
}
/**
* 处理单个参数校验异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Result<Object>> handleConstraintViolation(ConstraintViolationException e) {
String message = e.getConstraintViolations()
.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining("; "));
log.warn("参数校验失败: {}", message);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(Result.error(ResultCode.BAD_REQUEST.getCode(), message));
}
/**
* 处理参数类型不匹配异常
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Result<Object>> handleTypeMismatch(MethodArgumentTypeMismatchException e) {
String message = "参数类型不匹配";
log.warn("参数类型不匹配: {}", e.getMessage());
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(Result.error(ResultCode.BAD_REQUEST.getCode(), message));
}
/**
* 处理404异常
*/
@ExceptionHandler({NoHandlerFoundException.class, NoResourceFoundException.class})
public ResponseEntity<Result<Object>> handleNotFoundException(Exception e, WebRequest request) {
log.warn("请求资源不存在: {}", request.getDescription(false));
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(Result.error(ResultCode.NOT_FOUND.getCode(), "请求的资源不存在"));
}
/**
* 处理运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Result<Object>> handleRuntimeException(RuntimeException e, WebRequest request) {
log.error("系统运行时异常: {}", e.getMessage(), e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Result.error(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "系统内部错误,请稍后重试"));
}
/**
* 处理所有未捕获的异常
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<Result<Object>> handleException(Exception e, WebRequest request) {
log.error("系统异常: {}", e.getMessage(), e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Result.error(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "系统内部错误,请联系管理员"));
}
}
应用配置
# application.yml
spring:
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
server:
error:
include-message: always
测试示例
// TestController.java
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/test")
@Validated
public class TestController {
@PostMapping("/error")
public Result<Object> testBusinessException() {
throw new BusinessException(ResultCode.BAD_REQUEST, "业务处理失败");
}
@PostMapping("/valid")
public Result<UserDTO> testValid(@Valid @RequestBody UserDTO userDTO) {
return Result.success(userDTO);
}
@GetMapping("/param")
public Result<Object> testParamValidation(
@NotBlank(message = "用户名不能为空") @RequestParam String username,
@Email(message = "邮箱格式不正确") @RequestParam String email) {
return Result.success(Map.of("username", username, "email", email));
}
@GetMapping("/type")
public Result<Object> testTypeMismatch(@RequestParam Integer age) {
return Result.success(Map.of("age", age));
}
@GetMapping("/npe")
public Result<Object> testNPE() {
String str = null;
str.length(); // 触发空指针异常
return Result.success(null);
}
// 测试DTO
static class UserDTO {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@NotEmpty(message = "兴趣爱好不能为空")
private List<String> hobbies;
// getters and setters...
}
}
自定义异常处理器增强版
// EnhancedGlobalExceptionHandler.java
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class EnhancedGlobalExceptionHandler {
/**
* 完整的错误响应体,包含错误详情
*/
private Map<String, Object> buildErrorBody(Integer code, String message, HttpServletRequest request) {
Map<String, Object> errorBody = new HashMap<>();
errorBody.put("code", code);
errorBody.put("message", message);
errorBody.put("path", request.getRequestURI());
errorBody.put("timestamp", LocalDateTime.now().toString());
errorBody.put("method", request.getMethod());
return errorBody;
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Map<String, Object>> handleBusiness(BusinessException e) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
return ResponseEntity
.status(HttpStatus.OK)
.body(buildErrorBody(e.getCode(), e.getMessage(), request));
}
}
前端统一处理示例
// 前端统一错误处理
async function request(url, options = {}) {
const response = await fetch(url, {
headers: { 'Content-Type': 'application/json' },
...options
});
const result = await response.json();
if (result.code !== 200) {
// 统一错误提示
ElMessage.error(result.message);
// 401跳转登录
if (result.code === 401) {
window.location.href = '/login';
}
throw new Error(result.message);
}
return result.data;
}
使用说明
- 统一响应格式:所有成功和失败都返回
Result对象 - 业务异常:自定义
BusinessException用于业务逻辑错误 - 参数校验:使用
@Validated和@Valid结合异常处理 - 日志记录:异常都记录了日志,方便排查
- 错误信息:对外部返回友好提示,内部记录详细信息
这个全局异常处理方案能够覆盖项目中的绝大部分异常场景,具有很好的扩展性和维护性。