错误处理
在 Spring Boot 中,错误处理可以通过多种方式实现,从全局异常处理到自定义错误响应,以下是完整的错误处理方案:
1. 全局异常处理(推荐)
使用 @ControllerAdvice
+ @ExceptionHandler
统一捕获异常,返回结构化错误信息。
(1) 定义全局异常处理器
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
ErrorResponse error = new ErrorResponse(
ex.getErrorCode(),
ex.getMessage(),
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
// 处理所有未捕获的异常
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
ErrorResponse error = new ErrorResponse(
"SERVER_ERROR",
"Internal server error",
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 自定义错误响应体
class ErrorResponse {
private String code;
private String message;
private long timestamp;
// 构造方法/getters/setters
}
(2) 抛出自定义异常
// 自定义业务异常
public class BusinessException extends RuntimeException {
private String errorCode;
public BusinessException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
// getter
}
// 在Controller中使用
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
if (id == 0) {
throw new BusinessException("INVALID_ID", "ID cannot be zero");
}
return userService.findById(id);
}
2. HTTP 状态码控制
通过 ResponseEntity
或注解直接指定状态码:
(1) 使用 ResponseEntity
@GetMapping("/resource/{id}")
public ResponseEntity<?> getResource(@PathVariable Long id) {
return resourceRepository.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
(2) 使用 @ResponseStatus
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
3. 自定义错误页面(适用于传统Web应用)
(1) 静态错误页面
在 src/main/resources/static/error/
下添加:
• 404.html
(Not Found)
• 500.html
(Server Error)
(2) 动态模板错误页(Thymeleaf/FreeMarker)
@Controller
public class CustomErrorController implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Integer status = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (status == 404) {
return "error-404";
}
return "error-default";
}
}
4. 验证错误处理(@Valid)
自动处理 @Valid
参数校验失败的情况:
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody User user) {
// 正常逻辑
}
// 在GlobalExceptionHandler中添加
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
ErrorResponse error = new ErrorResponse(
"VALIDATION_FAILED",
String.join("; ", errors),
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
5. 异常日志记录
结合 @Slf4j
记录异常:
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
log.error("Unhandled exception: ", ex); // 记录完整堆栈
ErrorResponse error = new ErrorResponse(/* ... */);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
6. 特定Controller的异常处理
@RestController
public class UserController {
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
return new ErrorResponse(/* ... */);
}
}
7. 禁用默认的Whitelabel错误页
在 application.properties
中:
server.error.whitelabel.enabled=false
最佳实践总结
分层处理:
• 业务异常:自定义BusinessException
• 系统异常:全局捕获
Exception
• 校验异常:自动处理
MethodArgumentNotValidException
统一响应格式:
{ "code": "INVALID_ID", "message": "ID cannot be zero", "timestamp": 1630000000000, "path": "/api/users/0" }
生产环境安全:
• 避免暴露堆栈信息(可通过server.error.include-stacktrace=never
关闭)• 敏感错误信息过滤
通过以上方式,Spring Boot 的错误处理可以做到与 Laravel 的 Handler.php
类似的集中管理效果。