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

Spring WebFlux中@AfterReturning切面未正确触发问题求助

问题原因

Spring WebFlux 基于反应式编程模型,你的控制器方法返回的 Flux<Test> 只是一个未被订阅的数据流定义,此时业务逻辑(比如 service.addCars() 里的操作)还没有实际执行——反应式流只有在被订阅后才会触发真正的计算。而 @AfterReturning 切面是在控制器方法返回对象的瞬间就触发了,自然拿不到实际的响应 payload,只会捕获到空的数据流容器。

解决方案

下面是两种实用的解决方式:

方案1:用 WebFilter 全局拦截响应

WebFilter 可以在响应写入客户端的前后处理内容,适合做全局的响应日志:

@Component
public class ResponseLoggingFilter implements WebFilter {
    private static final Logger log = LoggerFactory.getLogger(ResponseLoggingFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        
        // 包装响应对象,捕获输出内容
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.doOnNext(dataBuffer -> {
                        // 读取响应字节并转换为字符串
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        DataBufferUtils.release(dataBuffer);
                        String responsePayload = new String(content, StandardCharsets.UTF_8);
                        log.info("response :: {}", responsePayload);
                    }));
                }
                return super.writeWith(body);
            }
        };

        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}

方案2:修改切面为 @Around,订阅反应式流

如果必须用切面实现,可以用 @Around 环绕通知,在拿到返回的 Mono/Flux 后,通过反应式操作符在数据流产生元素时记录:

@Aspect
@Component
public class ResponseLoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(ResponseLoggingAspect.class);

    @Around("pointCut()")
    public Object logResponse(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        
        // 针对反应式类型添加日志逻辑
        if (result instanceof Flux) {
            return ((Flux<?>) result).doOnNext(respValue -> {
                log.info("response :: {}", respValue);
            });
        } else if (result instanceof Mono) {
            return ((Mono<?>) result).doOnNext(respValue -> {
                log.info("response :: {}", respValue);
            });
        }
        
        // 非反应式类型直接返回
        return result;
    }
}

注意事项

  • 不要在切面中调用 block() 方法,这会阻塞反应式线程,完全破坏 WebFlux 的非阻塞特性。
  • 如果需要在整个数据流完成后记录(而非每个元素),可以替换 doOnNext 为 doOnComplete,但后者无法获取具体的响应元素。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 18:34:58