Skip to content

Commit c2c5352

Browse files
authored
Support Spring RestClient as default TransportClientFactory (#4281)
1 parent 6b68d64 commit c2c5352

24 files changed

+1379
-322
lines changed

docs/modules/ROOT/pages/spring-cloud-netflix.adoc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,11 @@ It is initialized in a `SmartLifecycle` (with `phase=0`), so the earliest you ca
270270

271271
==== Underlying HTTP clients
272272

273-
`EurekaClient` uses either `RestTemplate`, `WebClient` or `JerseyClient` under the hood. In order to use the `EurekaClient`, you need to have one of the supported HTTP clients on your classpath.
273+
`EurekaClient` uses either `RestClient`, `RestTemplate`, `WebClient` or `JerseyClient` under the hood. In order to use the `EurekaClient`, you need to have one of the supported HTTP clients on your classpath.
274274

275-
To use `RestTemplate`, add `spring-boot-starter-web` to your dependencies. To use `WebClient`, add `spring-boot-starter-webflux` to your dependencies. If both `RestTemplate` and `WebClient` are on the classpath when `eureka.client.webclient.enabled` is set to `true`, `WebClient` is used. Otherwise, `RestTemplate` is used.
275+
To use `RestTemplate` or `RestClient`, add `spring-boot-starter-web` to your dependencies. To use `WebClient`, add `spring-boot-starter-webflux` to your dependencies. If both `spring-boot-starter-web` and `spring-boot-starter-webflux` are included in the dependencies and the `eureka.client.webclient.enabled` flag is set to `true`, then `WebClient` will be used. If that's not the case and `eureka.client.restclient.enabled` is set to false, the `RestTemplate` will be used. Otherwise, the `RestClient` will be used.
276+
277+
NOTE: Starting from 4.2.0, the default client has changed to `RestClient`.
276278

277279
If you wish to use Jersey instead, you need to add the Jersey dependencies to your classpath.
278280
The following example shows the dependencies you need to add:

docs/modules/ROOT/partials/_configprops.adoc

Lines changed: 106 additions & 105 deletions
Large diffs are not rendered by default.

spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.apache.commons.logging.LogFactory;
2626

2727
import org.springframework.beans.factory.ObjectProvider;
28-
import org.springframework.beans.factory.annotation.Autowired;
2928
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
3029
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
3130
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -42,19 +41,23 @@
4241
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
4342
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
4443
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
44+
import org.springframework.cloud.netflix.eureka.http.RestClientDiscoveryClientOptionalArgs;
45+
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
4546
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
4647
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
4748
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
4849
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactories;
4950
import org.springframework.context.annotation.Bean;
5051
import org.springframework.context.annotation.Conditional;
5152
import org.springframework.context.annotation.Configuration;
53+
import org.springframework.web.client.RestClient;
5254
import org.springframework.web.reactive.function.client.WebClient;
5355

5456
/**
5557
* @author Daniel Lavoie
5658
* @author Armin Krezovic
5759
* @author Olga Maciaszek-Sharma
60+
* @author Wonchul Heo
5861
*/
5962
@Configuration(proxyBeanMethods = false)
6063
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
@@ -70,14 +73,14 @@ public TlsProperties tlsProperties() {
7073

7174
@Bean
7275
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
73-
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
76+
@Conditional(RestTemplateEnabledCondition.class)
7477
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class }, search = SearchStrategy.CURRENT)
75-
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
76-
havingValue = "false")
7778
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
7879
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
7980
ObjectProvider<RestTemplateBuilder> restTemplateBuilders) throws GeneralSecurityException, IOException {
80-
logger.info("Eureka HTTP Client uses RestTemplate.");
81+
if (logger.isInfoEnabled()) {
82+
logger.info("Eureka HTTP Client uses RestTemplate.");
83+
}
8184
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs(
8285
eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable);
8386
setupTLS(result, tlsProperties);
@@ -86,10 +89,8 @@ public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOption
8689

8790
@Bean
8891
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
89-
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
92+
@Conditional(RestTemplateEnabledCondition.class)
9093
@ConditionalOnMissingBean(value = { TransportClientFactories.class }, search = SearchStrategy.CURRENT)
91-
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
92-
havingValue = "false")
9394
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(
9495
RestTemplateDiscoveryClientOptionalArgs optionalArgs) {
9596
return new RestTemplateTransportClientFactories(optionalArgs);
@@ -118,7 +119,9 @@ static class DiscoveryClientOptionalArgsTlsConfiguration {
118119

119120
DiscoveryClientOptionalArgsTlsConfiguration(TlsProperties tlsProperties,
120121
AbstractDiscoveryClientOptionalArgs optionalArgs) throws GeneralSecurityException, IOException {
121-
logger.info("Eureka HTTP Client uses Jersey");
122+
if (logger.isInfoEnabled()) {
123+
logger.info("Eureka HTTP Client uses Jersey");
124+
}
122125
setupTLS(optionalArgs, tlsProperties);
123126
}
124127

@@ -129,16 +132,15 @@ static class DiscoveryClientOptionalArgsTlsConfiguration {
129132
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
130133
protected static class WebClientConfiguration {
131134

132-
@Autowired
133-
private TlsProperties tlsProperties;
134-
135135
@Bean
136136
@ConditionalOnMissingBean(
137137
value = { AbstractDiscoveryClientOptionalArgs.class, RestTemplateDiscoveryClientOptionalArgs.class },
138138
search = SearchStrategy.CURRENT)
139-
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(
139+
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
140140
ObjectProvider<WebClient.Builder> builder) throws GeneralSecurityException, IOException {
141-
logger.info("Eureka HTTP Client uses WebClient.");
141+
if (logger.isInfoEnabled()) {
142+
logger.info("Eureka HTTP Client uses WebClient.");
143+
}
142144
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs(
143145
builder::getIfAvailable);
144146
setupTLS(result, tlsProperties);
@@ -168,6 +170,33 @@ public WebClientNotFoundConfiguration() {
168170

169171
}
170172

173+
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
174+
@Conditional(RestClientEnabledCondition.class)
175+
protected static class RestClientConfiguration {
176+
177+
@Bean
178+
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
179+
search = SearchStrategy.CURRENT)
180+
public RestClientDiscoveryClientOptionalArgs restClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
181+
ObjectProvider<RestClient.Builder> builder) throws GeneralSecurityException, IOException {
182+
if (logger.isInfoEnabled()) {
183+
logger.info("Eureka HTTP Client uses RestClient.");
184+
}
185+
RestClientDiscoveryClientOptionalArgs result = new RestClientDiscoveryClientOptionalArgs(
186+
builder::getIfAvailable);
187+
setupTLS(result, tlsProperties);
188+
return result;
189+
}
190+
191+
@Bean
192+
@ConditionalOnMissingBean(value = TransportClientFactories.class, search = SearchStrategy.CURRENT)
193+
public RestClientTransportClientFactories restClientTransportClientFactories(
194+
ObjectProvider<RestClient.Builder> builder) {
195+
return new RestClientTransportClientFactories(builder::getIfAvailable);
196+
}
197+
198+
}
199+
171200
static class JerseyClientPresentAndEnabledCondition extends AllNestedConditions {
172201

173202
JerseyClientPresentAndEnabledCondition() {
@@ -204,4 +233,53 @@ static class OnJerseyClientDisabled {
204233

205234
}
206235

236+
static class RestTemplateEnabledCondition extends AllNestedConditions {
237+
238+
RestTemplateEnabledCondition() {
239+
super(ConfigurationPhase.REGISTER_BEAN);
240+
}
241+
242+
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
243+
static class OnJerseyClientNotPresentOrNotEnabled {
244+
245+
}
246+
247+
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
248+
havingValue = "false")
249+
static class OnWebClientDisabled {
250+
251+
}
252+
253+
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", havingValue = "false")
254+
static class OnRestClientDisabled {
255+
256+
}
257+
258+
}
259+
260+
static class RestClientEnabledCondition extends AllNestedConditions {
261+
262+
RestClientEnabledCondition() {
263+
super(ConfigurationPhase.REGISTER_BEAN);
264+
}
265+
266+
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
267+
static class OnJerseyClientNotPresentOrNotEnabled {
268+
269+
}
270+
271+
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
272+
havingValue = "false")
273+
static class OnWebClientDisabled {
274+
275+
}
276+
277+
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", matchIfMissing = true,
278+
havingValue = "true")
279+
static class OnRestClientDisabled {
280+
281+
}
282+
283+
}
284+
207285
}

spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2828
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
2929
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
3031
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
3132
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3233
import org.springframework.boot.web.client.RestTemplateBuilder;
@@ -37,6 +38,8 @@
3738
import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties;
3839
import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier;
3940
import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier;
41+
import org.springframework.cloud.netflix.eureka.http.RestClientEurekaHttpClient;
42+
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactory;
4043
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
4144
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
4245
import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient;
@@ -46,6 +49,7 @@
4649
import org.springframework.context.annotation.Configuration;
4750
import org.springframework.core.env.Environment;
4851
import org.springframework.lang.Nullable;
52+
import org.springframework.web.client.RestClient;
4953
import org.springframework.web.reactive.function.client.WebClient;
5054

5155
/**
@@ -54,6 +58,7 @@
5458
*
5559
* @author Dave Syer
5660
* @author Armin Krezovic
61+
* @author Wonchul Heo
5762
*/
5863
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
5964
@Conditional(EurekaConfigServerBootstrapConfiguration.EurekaConfigServerBootstrapCondition.class)
@@ -69,8 +74,7 @@ public EurekaClientConfigBean eurekaClientConfigBean() {
6974

7075
@Bean
7176
@ConditionalOnMissingBean(EurekaHttpClient.class)
72-
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
73-
havingValue = "false")
77+
@Conditional(RestTemplateEnabledCondition.class)
7478
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config,
7579
Environment env, @Nullable TlsProperties properties,
7680
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
@@ -109,6 +113,61 @@ public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient(Eureka
109113

110114
}
111115

116+
@Configuration(proxyBeanMethods = false)
117+
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
118+
@Conditional(RestClientEnabledCondition.class)
119+
@ImportAutoConfiguration(RestClientAutoConfiguration.class)
120+
protected static class RestClientConfiguration {
121+
122+
@Bean
123+
@ConditionalOnMissingBean(EurekaHttpClient.class)
124+
public RestClientEurekaHttpClient configDiscoveryRestClientEurekaHttpClient(EurekaClientConfigBean config,
125+
ObjectProvider<RestClient.Builder> builder, Environment env) {
126+
return (RestClientEurekaHttpClient) new RestClientTransportClientFactory(builder::getIfAvailable)
127+
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
128+
}
129+
130+
}
131+
132+
static class RestTemplateEnabledCondition extends AllNestedConditions {
133+
134+
RestTemplateEnabledCondition() {
135+
super(ConfigurationPhase.REGISTER_BEAN);
136+
}
137+
138+
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
139+
havingValue = "false")
140+
static class OnWebClientDisabled {
141+
142+
}
143+
144+
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", havingValue = "false")
145+
static class OnRestClientDisabled {
146+
147+
}
148+
149+
}
150+
151+
static class RestClientEnabledCondition extends AllNestedConditions {
152+
153+
RestClientEnabledCondition() {
154+
super(ConfigurationPhase.REGISTER_BEAN);
155+
}
156+
157+
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
158+
havingValue = "false")
159+
static class OnWebClientDisabled {
160+
161+
}
162+
163+
@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", matchIfMissing = true,
164+
havingValue = "true")
165+
static class OnRestClientDisabled {
166+
167+
}
168+
169+
}
170+
112171
static class EurekaConfigServerBootstrapCondition extends AllNestedConditions {
113172

114173
EurekaConfigServerBootstrapCondition() {

0 commit comments

Comments
 (0)