@ControllerAdvice
是Spring 3.2引入的注解,用于定义全局控制器增强组件,主要功能包括:
try-catch
块污染业务代码@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"服务器内部错误",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// 统一错误响应DTO
@Data
@AllArgsConstructor
class ErrorResponse {
private int status;
private String error;
private String message;
private long timestamp = System.currentTimeMillis();
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
"资源未找到",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 只处理指定包下的控制器
@ControllerAdvice(basePackages = "com.example.web.controllers")
// 只处理带有特定注解的控制器
@ControllerAdvice(annotations = RestController.class)
// 只处理指定类
@ControllerAdvice(assignableTypes = {UserController.class, ProductController.class})
Spring会按照最具体到最通用的顺序匹配@ExceptionHandler
:
@ExceptionHandler(FileUploadException.class) // 优先匹配
public ResponseEntity<?> handleFileUpload(FileUploadException ex) { ... }
@ExceptionHandler(IOException.class) // 次级匹配
public ResponseEntity<?> handleIO(IOException ex) { ... }
@ExceptionHandler(Exception.class) // 兜底处理
public ResponseEntity<?> handleGeneral(Exception ex) { ... }
通过注入请求对象获取更多信息:
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(
Exception ex,
WebRequest request) {
String path = ((ServletWebRequest)request).getRequest().getRequestURI();
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"处理请求[" + path + "]时发生错误",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(
BusinessException ex) {
ErrorResponse error = new ErrorResponse(
ex.getErrorCode(),
ex.getErrorType(),
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
@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(
HttpStatus.BAD_REQUEST.value(),
"参数校验失败",
errors.toString()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler({
AccessDeniedException.class,
AuthenticationException.class
})
public ResponseEntity<ErrorResponse> handleAuthException(
RuntimeException ex) {
HttpStatus status = ex instanceof AccessDeniedException
? HttpStatus.FORBIDDEN
: HttpStatus.UNAUTHORIZED;
ErrorResponse error = new ErrorResponse(
status.value(),
"权限不足",
ex.getMessage()
);
return new ResponseEntity<>(error, status);
}
建议将异常分为几大类分别处理:
异常类型 | 处理方式 | HTTP状态码 |
---|---|---|
业务异常 | 返回具体错误信息 | 400-499 |
系统异常 | 记录日志,返回通用错误 | 500 |
第三方服务异常 | 记录日志,返回服务不可用 | 503 |
参数校验异常 | 返回详细校验错误 | 400 |
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(
Exception ex,
HttpServletRequest request) {
log.error("请求[{} {}]处理失败",
request.getMethod(),
request.getRequestURI(),
ex);
// ...返回错误响应
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(
Exception ex,
Environment env) {
boolean isProd = Arrays.asList(env.getActiveProfiles())
.contains("prod");
String message = isProd
? "服务器错误,请联系管理员"
: ex.getMessage();
// ...返回错误响应
}
@ControllerAdvice
的工作原理:
@ControllerAdvice
标记的类会被ExceptionHandlerExceptionResolver
识别@ExceptionHandler
方法@ExceptionHandler
方法@ControllerAdvice
中的处理方法[DispatcherServlet]
|
v
[HandlerExceptionResolver]
|
|-- [ExceptionHandlerExceptionResolver] (处理@ExceptionHandler)
|-- [ResponseStatusExceptionResolver] (处理@ResponseStatus)
`-- [DefaultHandlerExceptionResolver] (处理Spring标准异常)
@ExceptionHandler
方法(当前控制器内)@ControllerAdvice
中的@ExceptionHandler
方法@ResponseStatus
异常可能原因:
@EnableWebMvc
或
@ControllerAdvice
类未被Spring管理@ExceptionHandler({IllegalArgumentException.class, IllegalStateException.class})
public ResponseEntity<?> handleIllegal(RuntimeException ex) {
// 统一处理多种相似异常
}
如需更复杂控制,可以实现HandlerExceptionResolver
:
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
// 自定义异常处理逻辑
return ...;
}
}
application.properties
:
server.error.include-message=always
server.error.include-stacktrace=on_param
server.error.include-binding-errors=always
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomErrorController implements ErrorController {
@RequestMapping
public ResponseEntity<ErrorResponse> handleError(HttpServletRequest request) {
HttpStatus status = getStatus(request);
ErrorResponse error = new ErrorResponse(
status.value(),
"自定义错误处理",
(String)request.getAttribute("javax.servlet.error.message")
);
return new ResponseEntity<>(error, status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer code = (Integer) request.getAttribute("javax.servlet.error.status_code");
return code != null ? HttpStatus.valueOf(code) : HttpStatus.INTERNAL_SERVER_ERROR;
}
}
@ControllerAdvice
扫描范围:精确指定basePackages@Async
@ExceptionHandler(ReportableException.class)
@Async
public CompletableFuture<ResponseEntity<?>> handleReportable(ReportableException ex) {
// 异步处理可报告异常
reportService.sendReport(ex);
return CompletableFuture.completedFuture(ResponseEntity.badRequest().build());
}
通过合理使用@ControllerAdvice
进行全局异常处理,可以显著提高Spring应用的健壮性和可维护性,同时为客户端提供一致的错误响应格式。