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

SpringBoot异步接口返回200但数据为空,两种方案均无效求排查

问题分析与解决方案

核心问题拆解

你的两个方案都返回空数据,本质原因出在异步方法的返回类型和执行时机不匹配:

  • 方案1:加了@Async但返回普通List<Movie>,Spring会把方法丢去线程池异步执行,但调用方(比如Controller)会立即拿到初始的空List——哪怕你在方法内部用join()等任务完成,调用方已经提前拿到返回值了。另外循环里重复调用completableFuture.get(),既冗余又额外触发等待。
  • 方案2:除了上述@Async返回普通类型的问题,还在allOf后用thenApplyAsync做异步填充,主线程直接返回空List,完全没等填充逻辑执行。

正确实现方式

方式一:用CompletableFuture原生管理异步(推荐)

去掉方法上的@Async,自己用CompletableFuture实现异步逻辑,返回CompletableFuture<List<Movie>>,让Spring MVC自动等待结果完成:

public CompletableFuture<List<Movie>> getMoviesOfHeroAsync() {
    String heroes = "aaa;bbb;ccc";

    // 把每个hero的查询任务转换成返回Movie列表的Future
    List<CompletableFuture<List<Movie>>> futureList = Arrays.stream(heroes.split(";"))
            .map(hero -> getMoviesOfHero(hero)
                    .thenApply(map -> map.values().stream()
                            .flatMap(List::stream)
                            .collect(Collectors.toList())))
            .collect(Collectors.toList());

    // 等待所有任务完成后合并结果
    return CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]))
            .thenApply(v -> futureList.stream()
                    .map(CompletableFuture::join) // allOf已确保完成,join不会阻塞
                    .flatMap(List::stream)
                    .collect(Collectors.toList()));
}

方式二:保留@Async但返回Future类型

如果一定要用Spring的@Async,方法必须返回Future或其子类型,让调用方能等待异步执行完成:

@Async(value = "threadPoolTaskExecutor")
public CompletableFuture<List<Movie>> getMoviesOfHeroAsync() {
    List<Movie> allMoviesOfHeroesList = new ArrayList<>();
    String heroes = "aaa;bbb;ccc";

    List<CompletableFuture<Map<String, List<Movie>>>> completableFutureList = Arrays.stream(heroes.split(";"))
            .map(this::getMoviesOfHero)
            .collect(Collectors.toList());

    // 等待所有异步任务完成
    CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[0])).join();

    // 收集结果,每个Future只调用一次get()
    for (CompletableFuture<Map<String, List<Movie>>> future : completableFutureList) {
        try {
            Map<String, List<Movie>> result = future.get();
            if (!result.isEmpty()) {
                result.values().stream()
                        .flatMap(List::stream)
                        .forEach(allMoviesOfHeroesList::add);
            }
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    return CompletableFuture.completedFuture(allMoviesOfHeroesList);
}

Controller层适配

不管用哪种方式,Controller端点都要返回CompletableFuture<List<Movie>>,Spring MVC会自动处理异步响应:

@GetMapping("/hero-movies")
public CompletableFuture<List<Movie>> getHeroMovies() {
    return yourService.getMoviesOfHeroAsync();
}

额外检查项

  1. 确保启动类/配置类加了@EnableAsync注解,且threadPoolTaskExecutor配置正确。
  2. 单独测试getMoviesOfHero方法,确认它能返回有效数据——如果这个方法本身返回空,最终结果也会是空的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:07:37