网站首页 > 技术文章 正文
在 Spring Boot 中,异常处理是构建健壮、可维护 Web 应用的关键部分。良好的异常处理机制可以统一返回格式、提升用户体验、便于调试和监控。
以下是 Spring Boot 中处理异常的完整指南和最佳实践:
一、核心注解
1. @ControllerAdvice
- 全局控制器增强,用于定义全局异常处理器。
- 可以作用于所有 @Controller 或 @RestController。
2. @ExceptionHandler
- 定义具体的异常处理方法。
- 可以在控制器内部使用(局部),也可以配合 @ControllerAdvice 使用(全局)。
二、全局异常处理(推荐方式)
示例:创建全局异常处理器
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.NoHandlerFoundException;
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理自定义业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
ErrorResponse error = new ErrorResponse(
"BUSINESS_ERROR",
e.getMessage(),
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
// 处理参数校验异常(如 @Valid)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
ErrorResponse error = new ErrorResponse(
"VALIDATION_ERROR",
message,
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
// 处理资源未找到(404)
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFoundException(NoHandlerFoundException e) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
"The requested resource was not found: " + e.getRequestURL(),
System.currentTimeMillis()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
// 处理空指针等系统异常
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<ErrorResponse> handleNullPointerException(NullPointerException e) {
ErrorResponse error = new ErrorResponse(
"SYSTEM_ERROR",
"A system error occurred.",
System.currentTimeMillis()
);
// 记录详细日志
// log.error("NullPointerException", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
// 处理所有未被捕获的异常(兜底)
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneralException(Exception e) {
ErrorResponse error = new ErrorResponse(
"INTERNAL_ERROR",
"An unexpected error occurred.",
System.currentTimeMillis()
);
// 记录完整异常堆栈
// log.error("Unhandled exception", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
定义统一的错误响应体
public class ErrorResponse {
private String code;
private String message;
private long timestamp;
public ErrorResponse(String code, String message, long timestamp) {
this.code = code;
this.message = message;
this.timestamp = timestamp;
}
// getter & setter
}
三、自定义业务异常
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
在业务逻辑中抛出:
@Service
public class UserService {
public User getUser(Long id) {
if (id <= 0) {
throw new BusinessException("Invalid user ID: " + id);
}
// ...
}
}
四、处理参数校验异常
1. 添加依赖(已包含在 web starter 中)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2. 在 DTO 上使用校验注解
public class CreateUserRequest {
@NotBlank(message = "Name is required")
private String name;
@Email(message = "Invalid email format")
private String email;
// getter & setter
}
3. 在 Controller 中启用校验
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
// ...
}
4. 全局处理 MethodArgumentNotValidException
如上文所示,在 GlobalExceptionHandler 中处理。
五、配置文件设置(可选)
# application.yml
spring:
# 启用精确的异常映射(可选)
web:
resources:
add-mappings: true
# 开发环境可开启详细错误信息(生产环境务必关闭!)
server:
error:
include-message: always
include-binding-errors: always
include-stacktrace: on_param
生产环境警告:不要暴露堆栈信息给前端,防止信息泄露。
六、特殊异常处理
1. 处理 404(NoHandlerFound)
# application.yml
spring:
mvc:
throw-exception-if-no-handler-found: true # 未找到处理器时抛出异常
web:
resources:
add-mappings: false # 禁用默认静态资源映射(可选)
然后在 @ControllerAdvice 中捕获 NoHandlerFoundException。
2. 处理 Spring Security 异常
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAccessDenied(AccessDeniedException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(...);
}
七、最佳实践
实践 | 说明 |
使用 @ControllerAdvice | 实现全局异常处理,避免重复代码 |
定义统一错误格式 | 前后端约定一致的错误结构 |
区分异常类型 | 业务异常、系统异常、校验异常分别处理 |
记录日志 | 特别是系统异常,便于排查问题 |
生产环境隐藏细节 | 不暴露堆栈、数据库错误等敏感信息 |
使用自定义异常 | 提高代码可读性和可维护性 |
返回合适的 HTTP 状态码 | 如 400、401、403、404、500 等 |
八、测试异常处理
@Test
public void shouldReturn400WhenInvalidInput() throws Exception {
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\": \"\", \"email\": \"invalid\"}"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value("VALIDATION_ERROR"));
}
九、高级:自定义 ErrorController(可选)
如果需要完全控制错误页面或响应,可以实现 ErrorController 接口(Spring Boot 2.3+ 推荐使用 implements ErrorAttributes 或 @ControllerAdvice)。
总结
Spring Boot 的异常处理流程:
请求 → Controller → Service → 抛出异常
↓
@ExceptionHandler (局部或全局)
↓
统一返回 JSON 错误响应
核心原则:
- 集中处理:使用 @ControllerAdvice 避免散落在各处的 try-catch。
- 类型化处理:不同异常返回不同状态码和提示。
- 用户友好:前端能根据 code 或 message 做相应处理。
- 安全可靠:不暴露系统内部细节。
猜你喜欢
- 2025-09-06 缩小字符串( Compact String)和 压缩字符串(Compressed String)
- 2025-09-06 通过冒泡排序测试Java和PHP性能_冒泡排序java解析
- 2025-09-06 Java对象拷贝原理剖析及最佳实践_java对象浅拷贝
- 2025-09-06 10 RDD 行动算子_rdd行动操作
- 2025-06-24 java文本对比工具源码1(java比较文本相似度)
- 2025-06-24 线上系统性能太差,我手写了字符串切割函数,性能提升10倍以上
- 2025-06-24 redis Scan 踩坑记 key的模糊匹配
- 2025-06-24 QT之QString(qty是什么单位的缩写)
- 2025-06-24 50个Java编程技巧,免费送给大家(java编程教程)
- 2025-06-24 Spring Boot中如何设计出一个高效的分布式并发锁?
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)