По сути, вы захотите сделать что-то вроде этого:
package com.example.demo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class GamesProcessor {
private static final String GAME_URI_BASE = "https://la2.api.riotgames.com/lol/match/v4/matches/";
private static final String ACCOUNT_URI_BASE = "https://la2.api.riotgames.com/lol/match/v4/matchlists/by-account/";
private Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() - 1);
@Autowired
private RestTemplate restTemplate;
public void processGames(String accountId) throws JsonProcessingException, ExecutionException, InterruptedException {
String responseAsString = restTemplate.getForObject(ACCOUNT_URI_BASE + accountId, String.class);
ObjectMapper objectMapper = new ObjectMapper();
if (responseAsString != null) {
Map<String, Object> response = objectMapper.readValue(responseAsString, new TypeReference<Map<String, Object>>() {
});
List<Map<String, Object>> matches = (List<Map<String, Object>>) ((Map<String, Object>) response.get("data")).get("matches");
List<CompletableFuture<Void>> futures = matches.stream()
.map(m -> (String) m.get("gameId"))
.map(gameId -> CompletableFuture.supplyAsync(() -> restTemplate.getForObject(GAME_URI_BASE + gameId, String.class), executor)
.thenAccept(r -> {
System.out.println(r); //do whatever you wish with the response here
}))
.collect(Collectors.toList());
// now we execute all requests asynchronously
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
}
}
}
Обратите внимание, что это не улучшенный код, а просто быстрый пример того, как этого добиться. В идеале вы должны заменить JSON обработку, которую я выполнял "вручную" с использованием Map, на компонент EJB, который соответствует структуре ответа, получаемого от вызываемой вами службы.
A быстрый просмотр:
String responseAsString = restTemplate.getForObject(ACCOUNT_URI_BASE + accountId, String.class);
Выполняет первый запрос REST и получает его в виде строки (ответ JSON). Вы захотите правильно отобразить это, используя объект Bean. Затем это обрабатывается с использованием ObjectMapper, предоставленного Джексоном, и преобразуется в карту, чтобы вы могли перемещаться по JSON и получать совпадения.
List<CompletableFuture<Void>> futures = matches.stream()
.map(m -> (String) m.get("gameId"))
.map(gameId -> CompletableFuture.supplyAsync(() -> restTemplate.getForObject(GAME_URI_BASE + gameId, String.class), executor)
.thenAccept(r -> {
System.out.println(r); //do whatever you wish with the response here
}))
.collect(Collectors.toList());
Как только у нас будут все совпадения, мы будем использовать Stream API для преобразовать их в CompletableFutures, которые будут выполняться асинхронно. Каждый поток сделает другой запрос, чтобы получить ответ для каждого отдельного matchId.
System.out.println(r);
Это будет выполняться для каждого ответа, который вы получаете для каждого matchId, как в вашем примере. Это также должно быть заменено соответствующим bean-компонентом, соответствующим выходу для более четкой обработки.
Обратите внимание, что List<CompletableFuture<Void>> futures
только "содержит код", но не будет выполняться, пока мы не объединим все в конце, используя CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
и выполнить метод блокировки get()
.