Skip to content

Commit 8f6a9b9

Browse files
OlgaMaciaszekrstoyanchevphilwebb
committed
Support declarative HTTP Service clients
Add auto-configuration support for Spring Framework's HTTP Service clients, supporting both `RestClient` and `WebClient` client types. Configuration properties are provided under `spring.http.client.service` and `spring.http.reactiveclient.service` names. Group specific properties are available under `....service.group.<name>.`. Auto-configuration support includes automatic registration of user defined `HttpServiceGroupConfigurer` beans. `RestClientCustomizer` and `WebClientCustomizer` beans are also supported. Closes gh-31337 Co-authored-by: Rossen Stoyanchev <[email protected]> Co-authored-by: Phillip Webb <[email protected]>
1 parent f1f5438 commit 8f6a9b9

21 files changed

+1651
-12
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/ClientHttpRequestFactories.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.util.function.Function;
2222
import java.util.function.Predicate;
2323

24-
import org.springframework.beans.factory.ObjectProvider;
24+
import org.springframework.beans.factory.ObjectFactory;
2525
import org.springframework.boot.autoconfigure.http.client.AbstractHttpClientProperties.Ssl;
2626
import org.springframework.boot.autoconfigure.http.client.AbstractHttpRequestFactoryProperties.Factory;
2727
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
@@ -37,25 +37,26 @@
3737
* {@link ClientHttpRequestFactorySettings}.
3838
*
3939
* @author Phillip Webb
40+
* @since 4.0.0
4041
*/
41-
class ClientHttpRequestFactories {
42+
public final class ClientHttpRequestFactories {
4243

43-
private final ObjectProvider<SslBundles> sslBundles;
44+
private final ObjectFactory<SslBundles> sslBundles;
4445

4546
private final AbstractHttpRequestFactoryProperties[] orderedProperties;
4647

47-
ClientHttpRequestFactories(ObjectProvider<SslBundles> sslBundles,
48+
public ClientHttpRequestFactories(ObjectFactory<SslBundles> sslBundles,
4849
AbstractHttpRequestFactoryProperties... orderedProperties) {
4950
this.sslBundles = sslBundles;
5051
this.orderedProperties = orderedProperties;
5152
}
5253

53-
ClientHttpRequestFactoryBuilder<?> builder(ClassLoader classLoader) {
54+
public ClientHttpRequestFactoryBuilder<?> builder(ClassLoader classLoader) {
5455
Factory factory = getProperty(AbstractHttpRequestFactoryProperties::getFactory);
5556
return (factory != null) ? factory.builder() : ClientHttpRequestFactoryBuilder.detect(classLoader);
5657
}
5758

58-
ClientHttpRequestFactorySettings settings() {
59+
public ClientHttpRequestFactorySettings settings() {
5960
HttpRedirects redirects = getProperty(AbstractHttpRequestFactoryProperties::getRedirects);
6061
Duration connectTimeout = getProperty(AbstractHttpRequestFactoryProperties::getConnectTimeout);
6162
Duration readTimeout = getProperty(AbstractHttpRequestFactoryProperties::getReadTimeout);

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/http/client/reactive/ClientHttpConnectors.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.util.function.Function;
2222
import java.util.function.Predicate;
2323

24-
import org.springframework.beans.factory.ObjectProvider;
24+
import org.springframework.beans.factory.ObjectFactory;
2525
import org.springframework.boot.autoconfigure.http.client.AbstractHttpClientProperties.Ssl;
2626
import org.springframework.boot.autoconfigure.http.client.reactive.AbstractClientHttpConnectorProperties.Connector;
2727
import org.springframework.boot.http.client.HttpRedirects;
@@ -36,25 +36,26 @@
3636
* {@link ClientHttpConnectorSettings}.
3737
*
3838
* @author Phillip Webb
39+
* @since 4.0.0
3940
*/
40-
class ClientHttpConnectors {
41+
public final class ClientHttpConnectors {
4142

42-
private final ObjectProvider<SslBundles> sslBundles;
43+
private final ObjectFactory<SslBundles> sslBundles;
4344

4445
private final AbstractClientHttpConnectorProperties[] orderedProperties;
4546

46-
ClientHttpConnectors(ObjectProvider<SslBundles> sslBundles,
47+
public ClientHttpConnectors(ObjectFactory<SslBundles> sslBundles,
4748
AbstractClientHttpConnectorProperties... orderedProperties) {
4849
this.sslBundles = sslBundles;
4950
this.orderedProperties = orderedProperties;
5051
}
5152

52-
ClientHttpConnectorBuilder<?> builder(ClassLoader classLoader) {
53+
public ClientHttpConnectorBuilder<?> builder(ClassLoader classLoader) {
5354
Connector connector = getProperty(AbstractClientHttpConnectorProperties::getConnector);
5455
return (connector != null) ? connector.builder() : ClientHttpConnectorBuilder.detect(classLoader);
5556
}
5657

57-
ClientHttpConnectorSettings settings() {
58+
public ClientHttpConnectorSettings settings() {
5859
HttpRedirects redirects = getProperty(AbstractClientHttpConnectorProperties::getRedirects);
5960
Duration connectTimeout = getProperty(AbstractClientHttpConnectorProperties::getConnectTimeout);
6061
Duration readTimeout = getProperty(AbstractClientHttpConnectorProperties::getReadTimeout);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2025 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.boot.autoconfigure.http.client.reactive.service;
18+
19+
import java.util.LinkedHashMap;
20+
import java.util.List;
21+
import java.util.Map;
22+
23+
import org.springframework.boot.autoconfigure.http.client.reactive.AbstractClientHttpConnectorProperties;
24+
25+
/**
26+
* {@link AbstractClientHttpConnectorProperties} for reactive HTTP Service clients.
27+
*
28+
* @author Olga Maciaszek-Sharma
29+
* @author Rossen Stoyanchev
30+
* @author Phillip Webb
31+
* @since 4.0.0
32+
*/
33+
public abstract class AbstractHttpReactiveClientServiceProperties extends AbstractClientHttpConnectorProperties {
34+
35+
/**
36+
* Base url to set in the underlying HTTP client group. By default, set to
37+
* {@code null}.
38+
*/
39+
private String baseUrl;
40+
41+
/**
42+
* Default request headers for interface client group. By default, set to empty
43+
* {@link Map}.
44+
*/
45+
private Map<String, List<String>> defaultHeader = new LinkedHashMap<>();
46+
47+
public String getBaseUrl() {
48+
return this.baseUrl;
49+
}
50+
51+
public void setBaseUrl(String baseUrl) {
52+
this.baseUrl = baseUrl;
53+
}
54+
55+
public Map<String, List<String>> getDefaultHeader() {
56+
return this.defaultHeader;
57+
}
58+
59+
public void setDefaultHeader(Map<String, List<String>> defaultHeaders) {
60+
this.defaultHeader = defaultHeaders;
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2012-2025 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.boot.autoconfigure.http.client.reactive.service;
18+
19+
import java.util.LinkedHashMap;
20+
import java.util.Map;
21+
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
24+
/**
25+
* Properties for Reactive HTTP Service clients.
26+
*
27+
* @author Olga Maciaszek-Sharma
28+
* @author Rossen Stoyanchev
29+
* @author Phillip Webb
30+
* @since 4.0.0
31+
*/
32+
@ConfigurationProperties("spring.http.reactiveclient.service")
33+
public class ReactiveHttpClientServiceProperties extends AbstractHttpReactiveClientServiceProperties {
34+
35+
/**
36+
* Group settings.
37+
*/
38+
private Map<String, Group> group = new LinkedHashMap<>();
39+
40+
public Map<String, Group> getGroup() {
41+
return this.group;
42+
}
43+
44+
public void setGroup(Map<String, Group> group) {
45+
this.group = group;
46+
}
47+
48+
/**
49+
* Properties for a single HTTP Service client group.
50+
*/
51+
public static class Group extends AbstractHttpReactiveClientServiceProperties {
52+
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2012-2025 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.boot.autoconfigure.http.client.reactive.service;
18+
19+
import org.springframework.beans.factory.BeanClassLoaderAware;
20+
import org.springframework.beans.factory.ObjectProvider;
21+
import org.springframework.boot.autoconfigure.AutoConfiguration;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
24+
import org.springframework.boot.autoconfigure.http.client.reactive.ClientHttpConnectorAutoConfiguration;
25+
import org.springframework.boot.autoconfigure.http.client.reactive.HttpReactiveClientProperties;
26+
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
27+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
28+
import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder;
29+
import org.springframework.boot.http.client.reactive.ClientHttpConnectorSettings;
30+
import org.springframework.boot.ssl.SslBundles;
31+
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
32+
import org.springframework.context.annotation.Bean;
33+
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
34+
import org.springframework.web.service.registry.HttpServiceProxyRegistry;
35+
import org.springframework.web.service.registry.ImportHttpServices;
36+
37+
/**
38+
* AutoConfiguration for Spring reactive HTTP Service Clients.
39+
* <p>
40+
* This will result in the creation of reactive HTTP Service client beans defined by
41+
* {@link ImportHttpServices @ImportHttpServices} annotations.
42+
*
43+
* @author Olga Maciaszek-Sharma
44+
* @author Rossen Stoyanchev
45+
* @author Phillip Webb
46+
* @since 4.0.0
47+
*/
48+
@AutoConfiguration(after = { ClientHttpConnectorAutoConfiguration.class, WebClientAutoConfiguration.class })
49+
@ConditionalOnClass(WebClientAdapter.class)
50+
@ConditionalOnBean(HttpServiceProxyRegistry.class)
51+
@EnableConfigurationProperties(ReactiveHttpClientServiceProperties.class)
52+
public class ReactiveHttpServiceClientAutoConfiguration implements BeanClassLoaderAware {
53+
54+
private ClassLoader beanClassLoader;
55+
56+
ReactiveHttpServiceClientAutoConfiguration() {
57+
}
58+
59+
@Override
60+
public void setBeanClassLoader(ClassLoader classLoader) {
61+
this.beanClassLoader = classLoader;
62+
}
63+
64+
@Bean
65+
WebClientPropertiesHttpServiceGroupConfigurer webClientPropertiesHttpServiceGroupConfigurer(
66+
ObjectProvider<SslBundles> sslBundles, HttpReactiveClientProperties httpReactiveClientProperties,
67+
ReactiveHttpClientServiceProperties serviceProperties,
68+
ObjectProvider<ClientHttpConnectorBuilder<?>> clientConnectorBuilder,
69+
ObjectProvider<ClientHttpConnectorSettings> clientConnectorSettings) {
70+
return new WebClientPropertiesHttpServiceGroupConfigurer(this.beanClassLoader, sslBundles,
71+
httpReactiveClientProperties, serviceProperties, clientConnectorBuilder, clientConnectorSettings);
72+
}
73+
74+
@Bean
75+
WebClientCustomizerHttpServiceGroupConfigurer webClientCustomizerHttpServiceGroupConfigurer(
76+
ObjectProvider<WebClientCustomizer> customizers) {
77+
return new WebClientCustomizerHttpServiceGroupConfigurer(customizers);
78+
}
79+
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2012-2025 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.boot.autoconfigure.http.client.reactive.service;
18+
19+
import org.springframework.beans.factory.ObjectProvider;
20+
import org.springframework.boot.web.client.RestClientCustomizer;
21+
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
22+
import org.springframework.web.client.RestClient;
23+
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;
24+
import org.springframework.web.reactive.function.client.WebClient;
25+
import org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer;
26+
27+
/**
28+
* A {@link RestClientHttpServiceGroupConfigurer} to apply auto-configured
29+
* {@link RestClientCustomizer} beans to the group's {@link RestClient}.
30+
*
31+
* @author Olga Maciaszek-Sharma
32+
* @author Phillip Webb
33+
*/
34+
class WebClientCustomizerHttpServiceGroupConfigurer implements WebClientHttpServiceGroupConfigurer {
35+
36+
/**
37+
* Allow user defined configurers to apply before / after ours.
38+
*/
39+
private static final int ORDER = 0;
40+
41+
private final ObjectProvider<WebClientCustomizer> customizers;
42+
43+
WebClientCustomizerHttpServiceGroupConfigurer(ObjectProvider<WebClientCustomizer> customizers) {
44+
this.customizers = customizers;
45+
}
46+
47+
@Override
48+
public int getOrder() {
49+
return ORDER;
50+
}
51+
52+
@Override
53+
public void configureGroups(Groups<WebClient.Builder> groups) {
54+
groups.configureClient(this::configureClient);
55+
}
56+
57+
private void configureClient(WebClient.Builder builder) {
58+
this.customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
59+
}
60+
61+
}

0 commit comments

Comments
 (0)