在AOP Around通知中如何正确处理返回Callable的方法?
问题根因
你当前的写法确实会导致call()方法执行两次:一次是切面内主动调用,另一次是Spring MVC处理异步返回值时的内部调用,这就是接口耗时翻倍的直接原因。
最优改造方案
不需要主动调用call(),只需对返回的Callable做一层包装,把日志逻辑嵌入到包装后的call()方法中,再把包装后的Callable返回给Spring MVC即可,改造成本极低,原有日志逻辑可以完全复用。
改造后的切面代码如下:
@Around("...") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); if (!(result instanceof Callable<?>)) { // 原有同步返回的日志逻辑无需修改 long cost = System.currentTimeMillis() - startTime; // 记录日志:耗时、响应结果、状态码等 return result; } else { Callable<?> originalCallable = (Callable<?>) result; // 包装原有Callable,嵌入日志逻辑 return (Callable<Object>) () -> { Object callableResult = originalCallable.call(); long cost = System.currentTimeMillis() - startTime; // 此处复用原有日志逻辑,callableResult就是实际响应结果 return callableResult; }; } }
该方案的原理是仅对Callable做功能增强,不会提前执行业务逻辑,Spring MVC后续处理异步请求时只会调用一次包装后的call()方法,同时完成业务逻辑执行和日志记录,没有额外性能损耗。
备选方案
如果不想在切面中处理异步逻辑,也可以使用Spring MVC自带的ResponseBodyAdvice统一拦截所有响应结果,无需关心控制器返回值类型,Spring会在响应返回给客户端前把最终响应体传入回调方法,直接在这里执行日志逻辑即可,示例代码如下:
@ControllerAdvice public class GlobalResponseLogAdvice implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 配置对所有返回值生效 return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // body就是最终的响应结果,直接在此处执行日志记录逻辑即可 return body; } }
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

