控制器抛出异常被全局处理后,为何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
相关产品推荐
相关产品推荐

