Spring 的 异常管理的相关注解@ControllerAdvice 和@ExceptionHandler
@ControllerAdvice 和 @ExceptionHandler 是 Spring 框架中用于全局异常处理的关键注解,它们可以帮助你优雅地处理应用程序中抛出的异常,避免直接将错误信息暴露给用户。下面详细介绍这两个注解的作用和用法:
一、全局异常处理的作用
在 Spring 应用中,当控制器方法抛出异常时,默认会返回 HTTP 500 错误(Internal Server Error),这对用户体验和调试都不友好。全局异常处理可以:
统一错误响应格式:将不同类型的异常转换为标准化的 JSON 或 XML 格式返回给客户端。
隐藏敏感信息:避免直接暴露堆栈跟踪或数据库错误等内部信息。
提高可维护性:将异常处理逻辑集中管理,避免在每个控制器中重复编写。
二、核心注解解析
1. @ControllerAdvice
作用:标记一个类为全局异常处理器,它可以拦截所有控制器中抛出的异常。
使用方式:
@ControllerAdvice
public class GlobalExceptionHandler {
// 异常处理方法
}
特性:
可通过basePackages属性指定拦截特定包下的控制器。
可与@RestControllerAdvice(等价于@ControllerAdvice + @ResponseBody)替换使用。
2. @ExceptionHandler
作用:定义具体的异常处理方法,用于捕获并处理特定类型的异常。
使用方式:
java
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleValidationException(ConstraintViolationException ex) {
// 处理异常逻辑
}
特性:
可指定多个异常类(如@ExceptionHandler({Exception1.class, Exception2.class}))。
方法返回值可以是ResponseEntity、视图名称或直接返回对象(需配合@ResponseBody)。
三、示例:完善的全局异常处理器
下面是一个更完整的全局异常处理器示例,包含常见的异常类型处理:
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.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理请求参数校验异常(@RequestBody参数)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>>
handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
// 处理请求参数校验异常(@PathVariable和@RequestParam参数)
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, String>> handleConstraintViolation(ConstraintViolationException ex) {
Map<String, String> errors = new HashMap<>();
ex.getConstraintViolations().forEach((violation) -> {
String propertyPath = violation.getPropertyPath().toString();
String errorMessage = violation.getMessage();
errors.put(propertyPath, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
// 处理BindException(表单数据绑定错误)
@ExceptionHandler(BindException.class)
public ResponseEntity<Map<String, String>> handleBindException(BindException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach((error) -> {
errors.put(error.getField(), error.getDefaultMessage());
});
return ResponseEntity.badRequest().body(errors);
}
// 处理其他未明确捕获的异常
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, String>> handleGeneralException(Exception ex) {
Map<String, String> error = new HashMap<>();
error.put("message", "Internal Server Error");
error.put("details", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
四、异常处理方法详解
1. MethodArgumentNotValidException
触发场景:当@RequestBody参数校验失败时抛出。
处理逻辑:提取字段错误信息,返回 HTTP 400(Bad Request)。
2. ConstraintViolationException
触发场景:当@PathVariable或@RequestParam参数校验失败时抛出。
处理逻辑:提取路径变量 / 请求参数的错误信息,返回 HTTP 400。
3. BindException
触发场景:当表单数据绑定时校验失败(如@ModelAttribute参数)。
处理逻辑:提取表单字段的错误信息,返回 HTTP 400。
4. 通用异常处理
触发场景:捕获所有未被明确处理的异常(如数据库异常、空指针等)。
处理逻辑:返回 HTTP 500(Internal Server Error),隐藏敏感信息。
五、自定义错误响应格式
为了统一 API 返回格式,通常会定义一个标准的错误响应类:
public class ErrorResponse {
private int status;
private String message;
private Map<String, String> details;
private long timestamp;
// 构造方法、getter和setter
}
修改异常处理方法,返回自定义格式:
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse>
handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
errors.put(((FieldError) error).getField(), error.getDefaultMessage());
});
ErrorResponse response = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"Validation Failed",
errors,
System.currentTimeMillis()
);
return ResponseEntity.badRequest().body(response);
}
六、总结
通过@ControllerAdvice和@ExceptionHandler,可以实现:
集中异常处理:将所有异常处理逻辑放在一个类中。
自定义错误响应:根据不同异常类型返回标准化的错误格式。
提升用户体验:避免直接暴露原始异常信息给客户端。
在实际项目中,建议根据业务需求扩展更多异常处理方法,如处理数据库异常(DataAccessException)、认证异常(AuthenticationException)等。同时,可以结合国际化资源文件(message.properties)实现多语言错误提示。