diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java index 1dbca9c1279..075f14227e4 100644 --- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientProperties.java @@ -16,10 +16,15 @@ package org.springframework.boot.http.client.autoconfigure.service; -import java.util.LinkedHashMap; +import java.util.Collections; +import java.util.Map; -import org.springframework.boot.context.properties.ConfigurationProperties; +import org.jspecify.annotations.Nullable; + +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.http.client.autoconfigure.HttpClientProperties; +import org.springframework.core.env.Environment; /** * Properties for HTTP Service clients. @@ -29,7 +34,28 @@ import org.springframework.boot.http.client.autoconfigure.HttpClientProperties; * @author Phillip Webb * @since 4.0.0 */ -@ConfigurationProperties("spring.http.serviceclient") -public class HttpServiceClientProperties extends LinkedHashMap { +public class HttpServiceClientProperties { + + private final Map properties; + + HttpServiceClientProperties(Map properties) { + this.properties = properties; + + } + + /** + * Return the {@link HttpClientProperties} for the given named client. + * @param name the service client name + * @return the properties or {@code null} + */ + public @Nullable HttpClientProperties get(String name) { + return this.properties.get(name); + } + + static HttpServiceClientProperties bind(Environment environment) { + return new HttpServiceClientProperties(Binder.get(environment) + .bind("spring.http.serviceclient", Bindable.mapOf(String.class, HttpClientProperties.class)) + .orElse(Collections.emptyMap())); + } } diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesAutoConfiguration.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesAutoConfiguration.java new file mode 100644 index 00000000000..c5fa10bea4e --- /dev/null +++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesAutoConfiguration.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.http.client.autoconfigure.service; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for + * {@link HttpServiceClientProperties}. + * + * @author Phillip Webb + * @since 4.0.2 + */ +@AutoConfiguration +public final class HttpServiceClientPropertiesAutoConfiguration { + + @Bean + HttpServiceClientProperties httpServiceClientProperties(Environment environment) { + return HttpServiceClientProperties.bind(environment); + } + +} diff --git a/module/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/module/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 9b4202ff6d7..21089ac72cc 100644 --- a/module/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/module/spring-boot-http-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -2,3 +2,4 @@ org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration org.springframework.boot.http.client.autoconfigure.imperative.ImperativeHttpClientAutoConfiguration org.springframework.boot.http.client.autoconfigure.metrics.HttpClientMetricsAutoConfiguration org.springframework.boot.http.client.autoconfigure.reactive.ReactiveHttpClientAutoConfiguration +org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientPropertiesAutoConfiguration diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java index 918586a43ee..8d22c97cd74 100644 --- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java +++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/autoconfigure/service/HttpServiceClientPropertiesTests.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.http.client.autoconfigure.HttpClientProperties; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.mock.env.MockEnvironment; @@ -50,28 +49,23 @@ class HttpServiceClientPropertiesTests { environment.setProperty("spring.http.serviceclient.c1.ssl.bundle", "usual"); environment.setProperty("spring.http.serviceclient.c2.base-url", "https://example.com/rossen"); environment.setProperty("spring.http.serviceclient.c3.base-url", "https://example.com/phil"); - try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext()) { - applicationContext.setEnvironment(environment); - applicationContext.register(PropertiesConfiguration.class); - applicationContext.refresh(); - HttpServiceClientProperties properties = applicationContext.getBean(HttpServiceClientProperties.class); - assertThat(properties).containsOnlyKeys("c1", "c2", "c3"); - HttpClientProperties c1 = properties.get("c1"); - assertThat(c1).isNotNull(); - assertThat(c1.getBaseUrl()).isEqualTo("https://example.com/olga"); - assertThat(c1.getDefaultHeader()).containsOnly(Map.entry("secure", List.of("very", "somewhat")), - Map.entry("test", List.of("true"))); - assertThat(c1.getRedirects()).isEqualTo(HttpRedirects.DONT_FOLLOW); - assertThat(c1.getConnectTimeout()).isEqualTo(Duration.ofSeconds(10)); - assertThat(c1.getReadTimeout()).isEqualTo(Duration.ofSeconds(20)); - assertThat(c1.getSsl().getBundle()).isEqualTo("usual"); - HttpClientProperties c2 = properties.get("c2"); - assertThat(c2).isNotNull(); - assertThat(c2.getBaseUrl()).isEqualTo("https://example.com/rossen"); - HttpClientProperties c3 = properties.get("c3"); - assertThat(c3).isNotNull(); - assertThat(c3.getBaseUrl()).isEqualTo("https://example.com/phil"); - } + HttpServiceClientProperties properties = HttpServiceClientProperties.bind(environment); + HttpClientProperties c1 = properties.get("c1"); + assertThat(c1).isNotNull(); + assertThat(c1.getBaseUrl()).isEqualTo("https://example.com/olga"); + assertThat(c1.getDefaultHeader()).containsOnly(Map.entry("secure", List.of("very", "somewhat")), + Map.entry("test", List.of("true"))); + assertThat(c1.getRedirects()).isEqualTo(HttpRedirects.DONT_FOLLOW); + assertThat(c1.getConnectTimeout()).isEqualTo(Duration.ofSeconds(10)); + assertThat(c1.getReadTimeout()).isEqualTo(Duration.ofSeconds(20)); + assertThat(c1.getSsl().getBundle()).isEqualTo("usual"); + HttpClientProperties c2 = properties.get("c2"); + assertThat(c2).isNotNull(); + assertThat(c2.getBaseUrl()).isEqualTo("https://example.com/rossen"); + HttpClientProperties c3 = properties.get("c3"); + assertThat(c3).isNotNull(); + assertThat(c3.getBaseUrl()).isEqualTo("https://example.com/phil"); + assertThat(properties.get("c4")).isNull(); } @Configuration diff --git a/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfiguration.java b/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfiguration.java index 7f3118f8491..1850e936f96 100644 --- a/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfiguration.java +++ b/module/spring-boot-restclient/src/main/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfiguration.java @@ -18,13 +18,14 @@ package org.springframework.boot.restclient.autoconfigure.service; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.autoconfigure.imperative.ImperativeHttpClientAutoConfiguration; import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientProperties; +import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientPropertiesAutoConfiguration; import org.springframework.boot.restclient.RestClientCustomizer; import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; import org.springframework.boot.ssl.SslBundles; @@ -35,17 +36,18 @@ import org.springframework.web.client.support.RestClientAdapter; import org.springframework.web.service.registry.HttpServiceProxyRegistry; /** - * AutoConfiguration for Spring HTTP Service clients backed by {@link RestClient}. + * {@link EnableAutoConfiguration Auto-configuration} for Spring HTTP Service clients + * backed by {@link RestClient}. * * @author Olga Maciaszek-Sharma * @author Rossen Stoyanchev * @author Phillip Webb * @since 4.0.0 */ -@AutoConfiguration(after = { ImperativeHttpClientAutoConfiguration.class, RestClientAutoConfiguration.class }) +@AutoConfiguration(after = { HttpServiceClientPropertiesAutoConfiguration.class, + ImperativeHttpClientAutoConfiguration.class, RestClientAutoConfiguration.class }) @ConditionalOnClass(RestClientAdapter.class) @ConditionalOnBean(HttpServiceProxyRegistry.class) -@EnableConfigurationProperties(HttpServiceClientProperties.class) public final class HttpServiceClientAutoConfiguration { @Bean diff --git a/module/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfigurationTests.java b/module/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfigurationTests.java index 11d0ca19bae..eec88a94909 100644 --- a/module/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfigurationTests.java +++ b/module/spring-boot-restclient/src/test/java/org/springframework/boot/restclient/autoconfigure/service/HttpServiceClientAutoConfigurationTests.java @@ -40,6 +40,7 @@ import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration; import org.springframework.boot.http.client.autoconfigure.imperative.ImperativeHttpClientAutoConfiguration; +import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientPropertiesAutoConfiguration; import org.springframework.boot.restclient.RestClientCustomizer; import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -68,7 +69,7 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers. import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; /** - * Tests for {@link HttpServiceClientAutoConfiguration}, + * Tests for {@link HttpServiceClientPropertiesAutoConfiguration}, * {@link PropertiesRestClientHttpServiceGroupConfigurer} and * {@link RestClientCustomizerHttpServiceGroupConfigurer}. * @@ -77,8 +78,9 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat class HttpServiceClientAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(HttpServiceClientAutoConfiguration.class, - ImperativeHttpClientAutoConfiguration.class, RestClientAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(HttpServiceClientPropertiesAutoConfiguration.class, + HttpServiceClientAutoConfiguration.class, ImperativeHttpClientAutoConfiguration.class, + RestClientAutoConfiguration.class)); @Test void configuresClientFromProperties() { diff --git a/module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfiguration.java b/module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfiguration.java index 2c4362567ab..24e0598c83f 100644 --- a/module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfiguration.java +++ b/module/spring-boot-webclient/src/main/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfiguration.java @@ -18,12 +18,13 @@ package org.springframework.boot.webclient.autoconfigure.service; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.autoconfigure.reactive.ReactiveHttpClientAutoConfiguration; import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientProperties; +import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientPropertiesAutoConfiguration; import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder; import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.webclient.WebClientCustomizer; @@ -35,17 +36,18 @@ import org.springframework.web.reactive.function.client.support.WebClientAdapter import org.springframework.web.service.registry.HttpServiceProxyRegistry; /** - * AutoConfiguration for Spring reactive HTTP Service Clients backed by {@link WebClient}. + * {@link EnableAutoConfiguration Auto-configuration} for Spring reactive HTTP Service + * Clients backed by {@link WebClient}. * * @author Olga Maciaszek-Sharma * @author Rossen Stoyanchev * @author Phillip Webb * @since 4.0.0 */ -@AutoConfiguration(after = { ReactiveHttpClientAutoConfiguration.class, WebClientAutoConfiguration.class }) +@AutoConfiguration(after = { HttpServiceClientPropertiesAutoConfiguration.class, + ReactiveHttpClientAutoConfiguration.class, WebClientAutoConfiguration.class }) @ConditionalOnClass(WebClientAdapter.class) @ConditionalOnBean(HttpServiceProxyRegistry.class) -@EnableConfigurationProperties(HttpServiceClientProperties.class) public final class ReactiveHttpServiceClientAutoConfiguration { @Bean diff --git a/module/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfigurationTests.java b/module/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfigurationTests.java index 527d0bc66aa..d486cccb1b8 100644 --- a/module/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfigurationTests.java +++ b/module/spring-boot-webclient/src/test/java/org/springframework/boot/webclient/autoconfigure/service/ReactiveHttpServiceClientAutoConfigurationTests.java @@ -34,6 +34,7 @@ import org.springframework.boot.http.client.HttpClientSettings; import org.springframework.boot.http.client.HttpRedirects; import org.springframework.boot.http.client.autoconfigure.HttpClientAutoConfiguration; import org.springframework.boot.http.client.autoconfigure.reactive.ReactiveHttpClientAutoConfiguration; +import org.springframework.boot.http.client.autoconfigure.service.HttpServiceClientPropertiesAutoConfiguration; import org.springframework.boot.http.client.reactive.ClientHttpConnectorBuilder; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.webclient.WebClientCustomizer; @@ -69,8 +70,9 @@ import static org.mockito.Mockito.mock; class ReactiveHttpServiceClientAutoConfigurationTests { private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(ReactiveHttpServiceClientAutoConfiguration.class, - ReactiveHttpClientAutoConfiguration.class, WebClientAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(HttpServiceClientPropertiesAutoConfiguration.class, + ReactiveHttpServiceClientAutoConfiguration.class, ReactiveHttpClientAutoConfiguration.class, + WebClientAutoConfiguration.class)); @Test void configuresClientFromProperties() {