Add Support to CompletableFuture as return type in http service method.
// gh-34748 Signed-off-by: Mengqi Xu <2663479778@qq.com>
This commit is contained in:
parent
12146c4a1b
commit
0c552b8652
|
@ -22,6 +22,7 @@ import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ import org.springframework.web.service.annotation.HttpExchange;
|
||||||
* @author Sebastien Deleuze
|
* @author Sebastien Deleuze
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Mengqi Xu
|
||||||
* @since 6.0
|
* @since 6.0
|
||||||
*/
|
*/
|
||||||
final class HttpServiceMethod {
|
final class HttpServiceMethod {
|
||||||
|
@ -412,7 +414,29 @@ final class HttpServiceMethod {
|
||||||
"Kotlin Coroutines are only supported with reactive implementations");
|
"Kotlin Coroutines are only supported with reactive implementations");
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodParameter param = new MethodParameter(method, -1).nestedIfOptional();
|
MethodParameter param = new MethodParameter(method, -1);
|
||||||
|
Class<?> paramType = param.getNestedParameterType();
|
||||||
|
|
||||||
|
Function<HttpRequestValues, @Nullable Object> responseFunction;
|
||||||
|
if (paramType.equals(CompletableFuture.class)) {
|
||||||
|
MethodParameter bodyParam = param.nested();
|
||||||
|
MethodParameter nestedParamIfOptional = bodyParam.getNestedParameterType().equals(Optional.class) ?
|
||||||
|
bodyParam.nested() : bodyParam;
|
||||||
|
responseFunction = request ->
|
||||||
|
CompletableFuture.supplyAsync(() ->
|
||||||
|
asOptionalIfNecessary(buildResponseFunction(client, nestedParamIfOptional).apply(request),
|
||||||
|
bodyParam.getNestedParameterType()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
responseFunction = request ->
|
||||||
|
asOptionalIfNecessary(buildResponseFunction(client, param.nestedIfOptional()).apply(request),
|
||||||
|
param.getParameterType());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExchangeResponseFunction(responseFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function<HttpRequestValues, @Nullable Object> buildResponseFunction(HttpExchangeAdapter client, MethodParameter param) {
|
||||||
Class<?> paramType = param.getNestedParameterType();
|
Class<?> paramType = param.getNestedParameterType();
|
||||||
|
|
||||||
Function<HttpRequestValues, @Nullable Object> responseFunction;
|
Function<HttpRequestValues, @Nullable Object> responseFunction;
|
||||||
|
@ -423,33 +447,30 @@ final class HttpServiceMethod {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (paramType.equals(HttpHeaders.class)) {
|
else if (paramType.equals(HttpHeaders.class)) {
|
||||||
responseFunction = request -> asOptionalIfNecessary(client.exchangeForHeaders(request), param);
|
responseFunction = client::exchangeForHeaders;
|
||||||
}
|
}
|
||||||
else if (paramType.equals(ResponseEntity.class)) {
|
else if (paramType.equals(ResponseEntity.class)) {
|
||||||
MethodParameter bodyParam = param.nested();
|
MethodParameter bodyParam = param.nested();
|
||||||
if (bodyParam.getNestedParameterType().equals(Void.class)) {
|
if (bodyParam.getNestedParameterType().equals(Void.class)) {
|
||||||
responseFunction = request ->
|
responseFunction = client::exchangeForBodilessEntity;
|
||||||
asOptionalIfNecessary(client.exchangeForBodilessEntity(request), param);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ParameterizedTypeReference<?> bodyTypeRef =
|
ParameterizedTypeReference<?> bodyTypeRef =
|
||||||
ParameterizedTypeReference.forType(bodyParam.getNestedGenericParameterType());
|
ParameterizedTypeReference.forType(bodyParam.getNestedGenericParameterType());
|
||||||
responseFunction = request ->
|
responseFunction = request -> client.exchangeForEntity(request, bodyTypeRef);
|
||||||
asOptionalIfNecessary(client.exchangeForEntity(request, bodyTypeRef), param);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ParameterizedTypeReference<?> bodyTypeRef =
|
ParameterizedTypeReference<?> bodyTypeRef =
|
||||||
ParameterizedTypeReference.forType(param.getNestedGenericParameterType());
|
ParameterizedTypeReference.forType(param.getNestedGenericParameterType());
|
||||||
responseFunction = request ->
|
responseFunction = request -> client.exchangeForBody(request, bodyTypeRef);
|
||||||
asOptionalIfNecessary(client.exchangeForBody(request, bodyTypeRef), param);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ExchangeResponseFunction(responseFunction);
|
return responseFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable Object asOptionalIfNecessary(@Nullable Object response, MethodParameter param) {
|
private static @Nullable Object asOptionalIfNecessary(@Nullable Object response, Class<?> type) {
|
||||||
return param.getParameterType().equals(Optional.class) ? Optional.ofNullable(response) : response;
|
return type.equals(Optional.class) ? Optional.ofNullable(response) : response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,8 @@ import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.core.Completable;
|
import io.reactivex.rxjava3.core.Completable;
|
||||||
import io.reactivex.rxjava3.core.Flowable;
|
import io.reactivex.rxjava3.core.Flowable;
|
||||||
|
@ -61,6 +63,7 @@ import static org.springframework.http.MediaType.APPLICATION_NDJSON_VALUE;
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Mengqi Xu
|
||||||
*/
|
*/
|
||||||
class HttpServiceMethodTests {
|
class HttpServiceMethodTests {
|
||||||
|
|
||||||
|
@ -103,6 +106,34 @@ class HttpServiceMethodTests {
|
||||||
assertThat(list).containsOnly("exchangeForBody");
|
assertThat(list).containsOnly("exchangeForBody");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-34748
|
||||||
|
void completableFutureService() throws ExecutionException, InterruptedException {
|
||||||
|
CompletableFutureService service = this.proxyFactory.createClient(CompletableFutureService.class);
|
||||||
|
|
||||||
|
service.execute();
|
||||||
|
|
||||||
|
HttpHeaders headers = service.getHeaders().get();
|
||||||
|
assertThat(headers).isNotNull();
|
||||||
|
|
||||||
|
String body = service.getBody().get();
|
||||||
|
assertThat(body).isEqualTo(this.client.getInvokedMethodName());
|
||||||
|
|
||||||
|
Optional<String> optional = service.getBodyOptional().get();
|
||||||
|
assertThat(optional.get()).isEqualTo("exchangeForBody");
|
||||||
|
|
||||||
|
ResponseEntity<String> entity = service.getEntity().get();
|
||||||
|
assertThat(entity.getBody()).isEqualTo("exchangeForEntity");
|
||||||
|
|
||||||
|
Optional<ResponseEntity<String>> entityOptional = service.getEntityOptional().get();
|
||||||
|
assertThat(entityOptional.get().getBody()).isEqualTo("exchangeForEntity");
|
||||||
|
|
||||||
|
ResponseEntity<Void> voidEntity = service.getVoidEntity().get();
|
||||||
|
assertThat(voidEntity.getBody()).isNull();
|
||||||
|
|
||||||
|
List<String> list = service.getList().get();
|
||||||
|
assertThat(list).containsOnly("exchangeForBody");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reactorService() {
|
void reactorService() {
|
||||||
ReactorService service = this.reactorProxyFactory.createClient(ReactorService.class);
|
ReactorService service = this.reactorProxyFactory.createClient(ReactorService.class);
|
||||||
|
@ -294,6 +325,35 @@ class HttpServiceMethodTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private interface CompletableFutureService {
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<Void> execute();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<HttpHeaders> getHeaders();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<String> getBody();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<Optional<String>> getBodyOptional();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<ResponseEntity<Void>> getVoidEntity();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<ResponseEntity<String>> getEntity();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<Optional<ResponseEntity<String>>> getEntityOptional();
|
||||||
|
|
||||||
|
@GetExchange
|
||||||
|
CompletableFuture<List<String>> getList();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private interface ReactorService {
|
private interface ReactorService {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue