diff --git a/spring-boot-project/spring-boot-autoconfigure/pom.xml b/spring-boot-project/spring-boot-autoconfigure/pom.xml
index da28b3c8b4..59de46a536 100755
--- a/spring-boot-project/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-project/spring-boot-autoconfigure/pom.xml
@@ -280,6 +280,11 @@
jetty-webapp
true
+
+ org.eclipse.jetty
+ jetty-reactive-httpclient
+ true
+
org.eclipse.jetty.websocket
javax-websocket-server-impl
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java
index 828970fd5e..e25157fc00 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java
@@ -39,7 +39,8 @@ import org.springframework.web.reactive.function.client.WebClient;
*/
@Configuration
@ConditionalOnClass(WebClient.class)
-@Import({ ClientHttpConnectorConfiguration.ReactorNetty.class })
+@Import({ ClientHttpConnectorConfiguration.ReactorNetty.class,
+ ClientHttpConnectorConfiguration.JettyClient.class })
public class ClientHttpConnectorAutoConfiguration {
@Bean
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java
index 1997e8b1f7..78493c20b7 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorConfiguration.java
@@ -23,6 +23,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
+import org.springframework.http.client.reactive.JettyClientHttpConnector;
+import org.springframework.http.client.reactive.JettyResourceFactory;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.client.reactive.ReactorResourceFactory;
@@ -59,4 +61,24 @@ class ClientHttpConnectorConfiguration {
}
+ @Configuration
+ @ConditionalOnClass(org.eclipse.jetty.reactive.client.ReactiveRequest.class)
+ @ConditionalOnMissingBean(ClientHttpConnector.class)
+ public static class JettyClient {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public JettyResourceFactory jettyResourceFactory() {
+ return new JettyResourceFactory();
+ }
+
+ @Bean
+ public JettyClientHttpConnector jettyClientHttpConnector(
+ JettyResourceFactory jettyResourceFactory) {
+ return new JettyClientHttpConnector(jettyResourceFactory, (httpClient) -> {
+ });
+ }
+
+ }
+
}
diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java
index 844d88b1af..56fad97f65 100644
--- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java
+++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java
@@ -21,6 +21,9 @@ import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.client.reactive.ReactorResourceFactory;
import org.springframework.web.reactive.function.client.WebClient;
@@ -56,4 +59,49 @@ public class ClientHttpConnectorAutoConfigurationTests {
});
}
+ @Test
+ public void shouldNotOverrideCustomClientConnector() {
+ this.contextRunner.withUserConfiguration(CustomClientHttpConnectorConfig.class)
+ .run((context) -> {
+ assertThat(context).hasSingleBean(ClientHttpConnector.class)
+ .hasBean("customConnector")
+ .doesNotHaveBean(ReactorResourceFactory.class);
+ WebClientCustomizer clientCustomizer = context
+ .getBean(WebClientCustomizer.class);
+ WebClient.Builder builder = mock(WebClient.Builder.class);
+ clientCustomizer.customize(builder);
+ verify(builder, times(1))
+ .clientConnector(any(ClientHttpConnector.class));
+ });
+ }
+
+ @Test
+ public void shouldUseCustomReactorResourceFactory() {
+ this.contextRunner.withUserConfiguration(CustomReactorResourceConfig.class)
+ .run((context) -> assertThat(context)
+ .hasSingleBean(ReactorClientHttpConnector.class)
+ .hasSingleBean(ReactorResourceFactory.class)
+ .hasBean("customReactorResourceFactory"));
+ }
+
+ @Configuration
+ static class CustomClientHttpConnectorConfig {
+
+ @Bean
+ public ClientHttpConnector customConnector() {
+ return mock(ClientHttpConnector.class);
+ }
+
+ }
+
+ @Configuration
+ static class CustomReactorResourceConfig {
+
+ @Bean
+ public ReactorResourceFactory customReactorResourceFactory() {
+ return new ReactorResourceFactory();
+ }
+
+ }
+
}
diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
index 5cf26f2298..820790838b 100644
--- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
@@ -5822,6 +5822,12 @@ The following code shows a typical example:
Spring Boot will auto-detect which `ClientHttpConnector` to drive `WebClient`, depending
on the libraries available on the application classpath.
+The `spring-boot-starter-webflux` depends on `io.projectreactor.netty:reactor-netty` by
+default, which brings both server and client implementations. If you choose to use Jetty
+as a reactive server instead, you should add a dependency on the Jetty Reactive HTTP
+client library, `org.eclipse.jetty:jetty-reactive-httpclient`, because it will
+automatically share HTTP resources with the server.
+
Developers can override this choice by defining their own `ClientHttpConnector` bean;
in this case, and depending on your HTTP client library of choice, you should also
define a resource factory bean that manages the HTTP resources for that client.