Skip to content

Commit e5989d5

Browse files
committed
Add WebClientBuilder and WebClientCustomizer
Even if WebFlux' `WebClient` has its own builder API, Spring Boot provides an extra layer on top of it with `WebClientBuilder`. This builder interface extends `WebClient.Builder` and provides an additional method to apply `WebClientCustomizer` instances to the client being built. With that infrastructure, Spring Boot can provide a stateless `WebClient` builder to the application context so that developers can create `WebClient` instances with Spring Boot conventions applied automatically. Closes spring-projectsgh-9522
1 parent 75274d3 commit e5989d5

File tree

8 files changed

+643
-4
lines changed

8 files changed

+643
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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.boot.autoconfigure.web.reactive.function.client;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.springframework.beans.factory.ObjectProvider;
23+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
26+
import org.springframework.boot.web.reactive.function.client.WebClientBuilder;
27+
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
31+
import org.springframework.util.CollectionUtils;
32+
import org.springframework.web.reactive.function.client.WebClient;
33+
34+
/**
35+
* {@link EnableAutoConfiguration Auto-configuration} for {@link WebClientBuilder}.
36+
* @author Brian Clozel
37+
* @since 2.0.0
38+
*/
39+
@Configuration
40+
@ConditionalOnClass(WebClient.class)
41+
public class WebClientAutoConfiguration {
42+
43+
private final ObjectProvider<List<WebClientCustomizer>> customizers;
44+
45+
public WebClientAutoConfiguration(ObjectProvider<List<WebClientCustomizer>> customizers) {
46+
this.customizers = customizers;
47+
}
48+
49+
@Bean
50+
@ConditionalOnMissingBean
51+
public WebClientBuilder webClientBuilder() {
52+
WebClientBuilder builder = new WebClientBuilder();
53+
List<WebClientCustomizer> customizers = this.customizers.getIfAvailable();
54+
if (!CollectionUtils.isEmpty(customizers)) {
55+
customizers = new ArrayList<>(customizers);
56+
AnnotationAwareOrderComparator.sort(customizers);
57+
builder = builder.customizers(customizers.toArray(new WebClientCustomizer[0]));
58+
}
59+
return builder;
60+
}
61+
}

spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,
114114
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
115115
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration,\
116116
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
117+
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
117118
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
118119
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
119120
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ public void shouldRegisterCustomHandlerMethodArgumentResolver() throws Exception
105105
.getBean(RequestMappingHandlerAdapter.class);
106106
assertThat((List<HandlerMethodArgumentResolver>) ReflectionTestUtils
107107
.getField(adapter.getArgumentResolverConfigurer(), "customResolvers"))
108-
.contains(
109-
this.context.getBean("firstResolver",
110-
HandlerMethodArgumentResolver.class),
108+
.contains(
109+
this.context.getBean("firstResolver",
110+
HandlerMethodArgumentResolver.class),
111111
this.context.getBean("secondResolver",
112112
HandlerMethodArgumentResolver.class));
113113
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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.boot.autoconfigure.web.reactive.function.client;
18+
19+
import org.junit.After;
20+
import org.junit.Test;
21+
22+
import org.springframework.boot.web.reactive.function.client.WebClientBuilder;
23+
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
24+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.web.reactive.function.client.WebClient;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
import static org.mockito.ArgumentMatchers.any;
31+
import static org.mockito.Mockito.mock;
32+
import static org.mockito.Mockito.verify;
33+
34+
/**
35+
* Tests for {@link WebClientAutoConfiguration}
36+
*
37+
* @author Brian Clozel
38+
*/
39+
public class WebClientAutoConfigurationTests {
40+
41+
private AnnotationConfigApplicationContext context;
42+
43+
@After
44+
public void close() {
45+
if (this.context != null) {
46+
this.context.close();
47+
}
48+
}
49+
50+
@Test
51+
public void webClientShouldApplyCustomizers() throws Exception {
52+
load(WebClientCustomizerConfig.class);
53+
WebClientBuilder webClientBuilder = this.context.getBean(WebClientBuilder.class);
54+
WebClientCustomizer customizer = this.context.getBean(WebClientCustomizer.class);
55+
webClientBuilder.build();
56+
verify(customizer).customize(any(WebClient.Builder.class));
57+
}
58+
59+
@Test
60+
public void shouldNotCreateClientBuilderIfAlreadyPresent() throws Exception {
61+
load(WebClientCustomizerConfig.class, CustomWebClientBuilderConfig.class);
62+
WebClientBuilder webClientBuilder = this.context.getBean(WebClientBuilder.class);
63+
assertThat(webClientBuilder).isInstanceOf(MyWebClientBuilder.class);
64+
}
65+
66+
private void load(Class<?>... config) {
67+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
68+
ctx.register(config);
69+
ctx.register(WebClientAutoConfiguration.class);
70+
ctx.refresh();
71+
this.context = ctx;
72+
}
73+
74+
@Configuration
75+
static class WebClientCustomizerConfig {
76+
77+
@Bean
78+
public WebClientCustomizer webClientCustomizer() {
79+
return mock(WebClientCustomizer.class);
80+
}
81+
82+
}
83+
84+
@Configuration
85+
static class CustomWebClientBuilderConfig {
86+
87+
@Bean
88+
public WebClientBuilder myWebClientBuilder() {
89+
return new MyWebClientBuilder();
90+
}
91+
92+
}
93+
94+
static class MyWebClientBuilder extends WebClientBuilder {
95+
96+
}
97+
98+
}

spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ org.springframework.boot.test.autoconfigure.web.client.WebClientRestTemplateAuto
108108
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
109109
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
110110
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
111-
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
111+
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
112+
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
112113

113114
# AutoConfigureWebMvc auto-configuration imports
114115
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc=\

0 commit comments

Comments
 (0)