You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:用于获取请求URI
  • HandlerMethod:用于获取控制器类名和方法名

步骤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);
}

注意事项

  1. 确保ErrorMessage类添加了对应的字段(requestUri、controllerClass、methodName)及Builder方法。
  2. HandlerMethod参数仅在异常由控制器方法直接抛出时才会被Spring注入,如果异常来自过滤器、拦截器或其他非控制器方法,该参数会为null,建议添加判空逻辑。
  3. 日志记录时要带上异常栈信息(即logger.error(..., e)),便于排查问题。

内容的提问来源于stack exchange,提问作者Ray Bond

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 01:39:52