From 49c463b1d26579d804da4dfc2814fe2aa0a2c8d7 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 13 Jul 2023 09:23:53 +0200 Subject: [PATCH] Polish RestClient request factories This commit changes the default request factory from the SimpleClientHttpRequestFactory to the JdkClientHttpRequestFactory if available. It also adds detection logic for OkHttp and Jetty. --- .../ROOT/pages/integration/rest-clients.adoc | 2 +- .../web/client/DefaultRestClientBuilder.java | 28 +++++++++++++++++++ .../web/client/RestClient.java | 9 ++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc b/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc index e20fdb52bc1..d5f8ac7df3b 100644 --- a/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc +++ b/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc @@ -13,7 +13,7 @@ The Spring Framework provides the following choices for making calls to REST end == `RestClient` Reference documentation is forthcoming. -For now, please refer to the https://docs.spring.io/spring-framework/docs/6.1.0-M1/javadoc-api/org/springframework/web/client/RestClient.html[API documentation]. +For now, please refer to the https://docs.spring.io/spring-framework/docs/6.1.0-M2/javadoc-api/org/springframework/web/client/RestClient.html[API documentation]. [[rest-webclient]] diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java b/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java index 4191770fca4..3f795edc4e0 100644 --- a/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java @@ -30,6 +30,9 @@ import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInitializer; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.JdkClientHttpRequestFactory; +import org.springframework.http.client.JettyClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; @@ -57,8 +60,18 @@ import org.springframework.web.util.UriBuilderFactory; */ final class DefaultRestClientBuilder implements RestClient.Builder { + // request factories + private static final boolean httpComponentsClientPresent; + private static final boolean okHttpClientPresent; + + private static final boolean jettyClientPresent; + + private static final boolean jdkClientPresent; + + // message factories + private static final boolean jackson2Present; private static final boolean gsonPresent; @@ -74,7 +87,12 @@ final class DefaultRestClientBuilder implements RestClient.Builder { static { ClassLoader loader = DefaultRestClientBuilder.class.getClassLoader(); + httpComponentsClientPresent = ClassUtils.isPresent("org.apache.hc.client5.http.classic.HttpClient", loader); + okHttpClientPresent = ClassUtils.isPresent("okhttp3.OkHttpClient", loader); + jettyClientPresent = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader); + jdkClientPresent = ClassUtils.isPresent("java.net.http.HttpClient", loader); + jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", loader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", loader); gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", loader); @@ -345,6 +363,16 @@ final class DefaultRestClientBuilder implements RestClient.Builder { else if (httpComponentsClientPresent) { return new HttpComponentsClientHttpRequestFactory(); } + else if (okHttpClientPresent) { + return new OkHttp3ClientHttpRequestFactory(); + } + else if (jettyClientPresent) { + return new JettyClientHttpRequestFactory(); + } + else if (jdkClientPresent) { + // java.net.http module might not be loaded, so we can't default to the JDK HttpClient + return new JdkClientHttpRequestFactory(); + } else { return new SimpleClientHttpRequestFactory(); } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestClient.java b/spring-web/src/main/java/org/springframework/web/client/RestClient.java index 39a317044d5..021c8252811 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestClient.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestClient.java @@ -346,6 +346,15 @@ public interface RestClient { * Configure the {@link ClientHttpRequestFactory} to use. This is useful * for plugging in and/or customizing options of the underlying HTTP * client library (e.g. SSL). + *

If no request factory is specified, {@code RestClient} uses + * {@linkplain org.springframework.http.client.HttpComponentsClientHttpRequestFactory Apache Http Client}, + * {@linkplain org.springframework.http.client.OkHttp3ClientHttpRequestFactory OkHttp 3}, or + * {@linkplain org.springframework.http.client.JettyClientHttpRequestFactory Jetty Http Client} + * if available on the classpath, and defaults to the + * {@linkplain org.springframework.http.client.JdkClientHttpRequestFactory JDK HttpClient} + * if the {@code java.net.http} module is loaded, or to a + * {@linkplain org.springframework.http.client.SimpleClientHttpRequestFactory simple default} + * otherwise. * @param requestFactory the request factory to use * @return this builder */