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

控制器抛出异常被全局处理后,为何AOP日志程序未执行?如何解决?

问题原因

@AfterReturning 是Spring AOP的后置返回通知,它的触发条件是目标方法正常执行完毕、无异常抛出并成功返回结果。

当你的Controller方法抛出FillDataException时,目标方法并没有走完正常的执行流程,而是直接抛出了异常,因此@AfterReturning对应的切面方法不会被触发。而全局异常处理器是由Spring MVC的异常处理机制在目标方法抛出异常后接管处理,这个过程已经脱离了目标方法的AOP生命周期,所以原有的AOP日志切面无法感知到异常处理后的返回结果。


解决方案

方案1:结合@AfterReturning与@AfterThrowing切面

分别处理正常返回和异常抛出的场景,覆盖两种日志记录需求:

@Aspect
@Component
public class LogAspect {

    @Pointcut("@annotation(com.test.common.log.RequestLog)")
    public void logPointCut() {
    }

    // 正常返回时记录日志
    @AfterReturning(value = "logPointCut()", returning = "ret")
    public void logAfterReturning(JoinPoint joinPoint, Object ret) {
        if (ret instanceof ApiResult) {
            // 保存正常返回的日志到数据库
            saveNormalLog(joinPoint, (ApiResult<?>) ret);
        }
    }

    // 抛出异常时记录日志
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        // 记录异常相关日志,比如异常类型、消息、请求方法信息等
        saveExceptionLog(joinPoint, e);
    }

    private void saveNormalLog(JoinPoint joinPoint, ApiResult<?> ret) {
        // 实现正常日志的保存逻辑
    }

    private void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 实现异常日志的保存逻辑
    }
}

注意:这种方式无法直接获取全局异常处理器返回的ApiResult,只能记录异常本身的信息,如果需要记录最终响应结果,推荐方案2。

方案2:使用ResponseBodyAdvice拦截响应(推荐)

Spring MVC提供的ResponseBodyAdvice可以在所有响应返回客户端之前拦截响应体,不管是Controller正常返回的结果,还是全局异常处理器处理后的结果,都能被捕获到,同时可以通过注解判断是否需要记录日志:

@ControllerAdvice
public class LogResponseAdvice implements ResponseBodyAdvice<ApiResult<?>> {

    // 仅拦截带有@RequestLog注解的方法的响应
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.hasMethodAnnotation(RequestLog.class);
    }

    @Override
    public ApiResult<?> beforeBodyWrite(ApiResult<?> body, MethodParameter returnType, MediaType selectedContentType,
                                        Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                        ServerHttpRequest request, ServerHttpResponse response) {
        // 这里的body就是最终返回的ApiResult,无论是正常返回还是异常处理后的
        saveLog(request, returnType, body);
        return body;
    }

    private void saveLog(ServerHttpRequest request, MethodParameter returnType, ApiResult<?> body) {
        // 示例:获取请求与响应的关键信息
        String requestUrl = request.getURI().getPath();
        String requestMethod = request.getMethodValue();
        RequestLog requestLog = returnType.getMethodAnnotation(RequestLog.class);
        String logDesc = requestLog.description();
        Integer responseCode = body.getCode();
        String responseMsg = body.getMessage();

        // 将上述信息保存到数据库
        // logRepository.save(new RequestLogPO(requestUrl, requestMethod, logDesc, responseCode, responseMsg));
    }
}

优势:

  • 完全解耦,无需修改原有AOP和全局异常处理器代码
  • 统一捕获所有需要记录日志的响应,日志信息更完整
  • 直接拿到最终返回的ApiResult,能记录响应的状态码、消息等关键内容

内容的提问来源于stack exchange,提问作者sk w

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 02:41:06