1
import java.util.ArrayList;
import java.util.concurrent.CompletionStage;
import play.libs.concurrent.HttpExecutionContext;
import play.libs.ws.WSClient;
import play.libs.ws.WSRequest;
import play.libs.ws.WSResponse;

class CustomerDetailedData {
    private String id;
    private String role;
    private String customerData;
}

class CustomRestClient {
    private final WSClient ws;
    private final HttpExecutionContext httpExecutionContext;

    @javax.inject.Inject
    public CustomRestClient(final WSClient ws, final HttpExecutionContext httpExecutionContext) {
        this.ws = ws;
        this.httpExecutionContext = httpExecutionContext;
    }

    public CompletionStage<RestResponse> get(final RestMessage restMessage) {
        return ws.url(restMessage).get()
                .thenApplyAsync(wsResponse -> {
                    return new RestResponse().setMsgBody(Json.parse(wsResponse.getBody()));
                }, httpExecutionContext.current());
    }
}

public class CompletionStagesExample {
    @Inject
    private CustomRestClient customRestClient;

    public static CompletionStage<CustomerDetailedData > getData(String url, String role, String id) {
        return customRestClient.get(new RestMessage(url, role, id))
                .thenApply(restResponse -> {
                    CustomerDetailedData customerData = convertToCustomerData(restResponse);
                    //here the role arg does not match response received by the get method 
                    //(we got here response for roleC, but role argument of getData method has "roleB" value on the current step)
                    customerData.setRole(role); 
                    return customerData;
                });
    }

    public static void main(String[] args) {
        final List<CompletableFuture<CustomerDetailedData>> futures = new ArrayList<>();

        futures.add(getData("url for role A", "RoleA", "123123a").toCompletableFuture());
        futures.add(getData("url for role B", "RoleB", "123123b").toCompletableFuture());
        futures.add(getData("url for role C", "RoleC", "123123c").toCompletableFuture());

        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .thenApply(aVoid -> {
                    final List<CustomerDetailedData> detailList = new ArrayList<>();
                    futures.stream()
                            .map(CompletableFuture::join)
                            .forEach(System.out::println);
                });
    }
}

输出可能如下所示(每次都可能不同):

[id = "123123a", role = "roleB", customerData = "服务器返回的角色 A 的一些数据"], [id = "123123b", role = "roleC", customerData = "角色 B 返回的一些数据server"], [id = "123123c", role = "roleA", customerData = "服务器返回的角色 C 的一些数据"]

预期结果:

[id = "123123a", role = "roleA", customerData = "服务器返回的角色 A 的一些数据"], [id = "123123b", role = "roleB", customerData = "角色 B 返回的一些数据server"], [id = "123123c", role = "roleC", customerData = "服务器返回的角色 C 的一些数据"]

我建议在 thenApply 主体中的 getData 方法中的角色搞砸(不匹配 id),因为 thenApply 访问了 getData 方法参数的错误示例(它们正在更改顺序,因为 thenApply 在 WSRequest 返回的另一个线程中执行)。

请看我在代码中的评论。

有谁知道Java是否保证放置在thenApply中的代码总是可以访问方法参数的正确示例(在我的例子中是getData方法的参数)?

4

0 回答 0