Spring Boot 3中如何在Controller Advice中获取HandlerMethod信息
问题
在Spring Boot 3中,我通过@ControllerAdvice处理数据库唯一性异常,生成可转为JSON并记录日志的自定义错误信息。现在需要补充请求URI、控制器类名、方法名到错误信息和日志中。目前已实现的代码如下:
控制器代码
/** * Update Car (PUT) */ @PutMapping(value = "/{id}", consumes = "application/json") ResponseEntity<CarCreateDto> updateCarDto(@Valid @RequestBody CarCreateDto updateCar, @PathVariable Long id) { CarCreateDto car = carService.updateCar(updateCar, id); return new ResponseEntity<>(car, HttpStatus.OK); }
异常处理器代码
@ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity dataIntegrityViolantionException(final DataIntegrityViolationException e) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(df); ErrorMessage errorMessage = new ErrorMessage(); String mostSpecificCauseMessage = e.getMostSpecificCause().getMessage(); if (e.getCause().getCause() instanceof SQLIntegrityConstraintViolationException) { try { String jsonMessage = objectMapper.writeValueAsString(errorMessage.builder() .reasonCode(ErrorMessage.ReasonCode.NAME_ALREADY_EXISTS) .message(mostSpecificCauseMessage) .timeStamp(df.format(System.currentTimeMillis())) .build()); return new ResponseEntity<>(jsonMessage, HttpStatus.CONFLICT); } catch (JsonProcessingException ex) { throw new RuntimeException(ex); } } return new ResponseEntity<>("Unknown Data Integrity Violation Exception with message : " + mostSpecificCauseMessage, HttpStatus.UNPROCESSABLE_ENTITY); }
我参考过类似示例,但没找到如何获取对应的HandlerMethod实例来提取控制器类名和方法名。请问是否需要在控制器方法中添加try-catch并手动传入HandlerMethod?
解决方案
不需要在控制器中添加try-catch,Spring提供了更简洁的方式:直接在异常处理器方法的参数列表中注入HandlerMethod和HttpServletRequest,Spring会自动传入当前请求对应的实例。
步骤1:修改异常处理器方法的参数
在异常处理器方法中添加两个参数:
HttpServletRequest:用于获取请求URIHandlerMethod:用于获取控制器类名和方法名
步骤2:提取并添加信息到错误对象
从上述参数中提取所需信息,补充到ErrorMessage中,同时可用于日志记录(建议使用SLF4J日志框架)。
修改后的完整异常处理器代码
import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.method.HandlerMethod; // 注入日志实例 private static final Logger logger = LoggerFactory.getLogger(YourControllerAdviceClass.class); @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity<?> dataIntegrityViolationException(final DataIntegrityViolationException e, HttpServletRequest request, HandlerMethod handlerMethod) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(df); String mostSpecificCauseMessage = e.getMostSpecificCause().getMessage(); // 提取请求与控制器信息 String requestUri = request.getRequestURI(); String controllerClassName = handlerMethod.getBeanType().getSimpleName(); String methodName = handlerMethod.getMethod().getName(); // 记录日志 logger.error("数据完整性异常 - URI: {}, 控制器: {}, 方法: {}, 原因: {}", requestUri, controllerClassName, methodName, mostSpecificCauseMessage, e); if (e.getCause().getCause() instanceof SQLIntegrityConstraintViolationException) { try { String jsonMessage = objectMapper.writeValueAsString(errorMessage.builder() .reasonCode(ErrorMessage.ReasonCode.NAME_ALREADY_EXISTS) .message(mostSpecificCauseMessage) .timeStamp(df.format(System.currentTimeMillis())) .requestUri(requestUri) .controllerClass(controllerClassName) .methodName(methodName) .build()); return new ResponseEntity<>(jsonMessage, HttpStatus.CONFLICT); } catch (JsonProcessingException ex) { logger.error("序列化错误信息失败", ex); throw new RuntimeException(ex); } } String errorMsg = String.format("未知数据完整性异常 - URI: %s, 控制器: %s, 方法: %s, 原因: %s", requestUri, controllerClassName, methodName, mostSpecificCauseMessage); logger.error(errorMsg, e); return new ResponseEntity<>(errorMsg, HttpStatus.UNPROCESSABLE_ENTITY); }
注意事项
- 确保
ErrorMessage类添加了对应的字段(requestUri、controllerClass、methodName)及Builder方法。 HandlerMethod参数仅在异常由控制器方法直接抛出时才会被Spring注入,如果异常来自过滤器、拦截器或其他非控制器方法,该参数会为null,建议添加判空逻辑。- 日志记录时要带上异常栈信息(即
logger.error(..., e)),便于排查问题。
内容的提问来源于stack exchange,提问作者Ray Bond
相关产品推荐
相关产品推荐

