Skip to content

Commit c8fe5d3

Browse files
Use SmartInitializingSingleton instead of BeanPostProcessor to set up @LoadBalanced WebClient.Builder. (#1319)
1 parent 9908098 commit c8fe5d3

File tree

6 files changed

+104
-14
lines changed

6 files changed

+104
-14
lines changed

spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
import org.springframework.beans.factory.ObjectProvider;
2424
import org.springframework.beans.factory.SmartInitializingSingleton;
2525
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.boot.autoconfigure.AutoConfiguration;
2627
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
2728
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2829
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -32,12 +33,12 @@
3233
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3334
import org.springframework.cloud.client.ServiceInstance;
3435
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
35-
import org.springframework.context.ApplicationContext;
3636
import org.springframework.context.annotation.Bean;
3737
import org.springframework.context.annotation.Conditional;
3838
import org.springframework.context.annotation.Configuration;
3939
import org.springframework.http.client.ClientHttpRequestInterceptor;
4040
import org.springframework.retry.support.RetryTemplate;
41+
import org.springframework.web.client.RestClient;
4142
import org.springframework.web.client.RestTemplate;
4243

4344
/**
@@ -49,7 +50,7 @@
4950
* @author Gang Li
5051
* @author Olga Maciaszek-Sharma
5152
*/
52-
@Configuration(proxyBeanMethods = false)
53+
@AutoConfiguration
5354
@Conditional(BlockingRestClassesPresentCondition.class)
5455
@ConditionalOnBean(LoadBalancerClient.class)
5556
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
@@ -59,6 +60,10 @@ public class LoadBalancerAutoConfiguration {
5960
@Autowired(required = false)
6061
private List<RestTemplate> restTemplates = Collections.emptyList();
6162

63+
@LoadBalanced
64+
@Autowired(required = false)
65+
private List<RestClient.Builder> restClientBuilders = Collections.emptyList();
66+
6267
@Autowired(required = false)
6368
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
6469

@@ -74,6 +79,19 @@ public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
7479
});
7580
}
7681

82+
@Bean
83+
public SmartInitializingSingleton loadBalancedRestClientBuilderInitializer(
84+
final ObjectProvider<List<RestClientBuilderCustomizer>> restClientBuilderCustomizers) {
85+
return () -> restClientBuilderCustomizers.ifAvailable(customizers -> {
86+
for (RestClient.Builder restClientBuilder : LoadBalancerAutoConfiguration.this.restClientBuilders) {
87+
for (RestClientBuilderCustomizer customizer : customizers) {
88+
customizer.customize(restClientBuilder);
89+
}
90+
91+
}
92+
});
93+
}
94+
7795
@Bean
7896
@ConditionalOnMissingBean
7997
public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
@@ -101,11 +119,10 @@ public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerIntercept
101119
}
102120

103121
@Bean
104-
@ConditionalOnBean(LoadBalancerInterceptor.class)
105122
@ConditionalOnMissingBean
106-
LoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor(
107-
final LoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) {
108-
return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context);
123+
public RestClientBuilderCustomizer restClientBuilderCustomizer(
124+
final LoadBalancerInterceptor loadBalancerInterceptor) {
125+
return restClientBuilder -> restClientBuilder.requestInterceptor(loadBalancerInterceptor);
109126
}
110127

111128
}
@@ -174,11 +191,10 @@ public RestTemplateCustomizer restTemplateCustomizer(
174191
}
175192

176193
@Bean
177-
@ConditionalOnBean(RetryLoadBalancerInterceptor.class)
178194
@ConditionalOnMissingBean
179-
LoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor(
180-
final RetryLoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) {
181-
return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context);
195+
public RestClientBuilderCustomizer restClientBuilderCustomizer(
196+
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
197+
return restClientBuilder -> restClientBuilder.requestInterceptor(loadBalancerInterceptor);
182198
}
183199

184200
}

spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerRestClientBuilderBeanPostProcessor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,7 +28,9 @@
2828
*
2929
* @author Olga Maciaszek-Sharma
3030
* @since 4.1.0
31+
* @deprecated to be removed in the next release.
3132
*/
33+
@Deprecated
3234
public class LoadBalancerRestClientBuilderBeanPostProcessor implements BeanPostProcessor {
3335

3436
private final ClientHttpRequestInterceptor loadBalancerInterceptor;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.client.loadbalancer;
18+
19+
import org.springframework.web.client.RestClient;
20+
21+
/**
22+
* A customizer interface for {@link RestClient.Builder}. Used to set
23+
* {@link LoadBalancerInterceptor} on the builder at the end of the singleton
24+
* pre-instantiation phase.
25+
*
26+
* @author Olga Maciaszek-Sharma
27+
* @since 4.1.1
28+
*/
29+
public interface RestClientBuilderCustomizer {
30+
31+
void customize(RestClient.Builder restClientBuilder);
32+
33+
}

spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/AbstractLoadBalancerAutoConfigurationTests.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -117,6 +117,43 @@ void multipleRestClientBuilders() {
117117
});
118118
}
119119

120+
@Test
121+
void restTemplatesAndRestClientsFromUsersAutoConfiguration() {
122+
applicationContextRunner
123+
.withConfiguration(AutoConfigurations.of(TwoRestTemplatesAndTwoRestClientBuilders.class))
124+
.run(context -> {
125+
final Map<String, RestClient.Builder> restClientBuilders = context
126+
.getBeansOfType(RestClient.Builder.class);
127+
final Map<String, RestTemplate> restTemplates = context.getBeansOfType(RestTemplate.class);
128+
129+
assertThat(restClientBuilders).isNotNull();
130+
assertThat(restClientBuilders.values()).hasSize(2);
131+
132+
TwoRestTemplatesAndTwoRestClientBuilders.Two two = context
133+
.getBean(TwoRestTemplatesAndTwoRestClientBuilders.Two.class);
134+
135+
assertThat(two.loadBalancedRestClientBuilder).isNotNull();
136+
assertLoadBalanced(two.loadBalancedRestClientBuilder);
137+
138+
assertThat(two.nonLoadBalancedRestClientBuilder).isNotNull();
139+
two.nonLoadBalancedRestClientBuilder
140+
.requestInterceptors(interceptors -> assertThat(interceptors).isEmpty());
141+
142+
assertThat(restTemplates).isNotNull();
143+
Collection<RestTemplate> templates = restTemplates.values();
144+
assertThat(templates).hasSize(2);
145+
146+
TwoRestTemplatesAndTwoRestClientBuilders.Two twoRestTemplate = context
147+
.getBean(TwoRestTemplatesAndTwoRestClientBuilders.Two.class);
148+
149+
assertThat(twoRestTemplate.loadBalanced).isNotNull();
150+
assertLoadBalanced(twoRestTemplate.loadBalanced);
151+
152+
assertThat(twoRestTemplate.nonLoadBalanced).isNotNull();
153+
assertThat(twoRestTemplate.nonLoadBalanced.getInterceptors()).isEmpty();
154+
});
155+
}
156+
120157
protected abstract void assertLoadBalanced(RestClient.Builder restClientBuilder);
121158

122159
protected abstract void assertLoadBalanced(RestTemplate restTemplate);

spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/support/LoadBalancerEnvironmentPropertyUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
public final class LoadBalancerEnvironmentPropertyUtils {
2525

2626
private LoadBalancerEnvironmentPropertyUtils() {
27-
throw new IllegalStateException("Should not instantiate a utility class");
27+
throw new UnsupportedOperationException("Cannot instantiate a utility class");
2828
}
2929

3030
public static boolean trueForClientOrDefault(Environment environment, String propertySuffix) {

src/checkstyle/checkstyle-suppressions.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@
1818
<suppress files=".*Tests.*" checks="JavadocVariable"/>
1919
<suppress files=".*Tests.*" checks="JavadocMethod"/>
2020
<suppress files=".*Tests.*" checks="HideUtilityClassConstructor"/>
21+
<suppress files=".*AutoConfiguration.*" checks="HideUtilityClassConstructor"/>
22+
<suppress files=".*AutoConfiguration.*" checks="FinalClass"/>
2123
<suppress files=".*ReactiveDiscoveryClient.*" checks="JavadocVariable"/>
2224
</suppressions>

0 commit comments

Comments
 (0)