From 5e966f957666c804b923a1e867d62b84e722bda0 Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Wed, 2 Apr 2025 18:48:39 +0300 Subject: [PATCH] Polish RestClientAutoConfiguration and RestClientBuilderConfigurer See gh-44982 Signed-off-by: Dmytro Nosan --- .../client/RestClientAutoConfiguration.java | 11 ++-- .../client/RestClientBuilderConfigurer.java | 33 +++++------ .../RestClientAutoConfigurationTests.java | 56 ++++++++++++++++++- .../RestClientBuilderConfigurerTests.java | 44 ++++++++++----- 4 files changed, 104 insertions(+), 40 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java index af44b3ae12a..f89fc31b660 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -79,11 +79,10 @@ public class RestClientAutoConfiguration { ObjectProvider> clientHttpRequestFactoryBuilder, ObjectProvider clientHttpRequestFactorySettings, ObjectProvider customizerProvider) { - RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); - configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable()); - configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable()); - configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList()); - return configurer; + return new RestClientBuilderConfigurer( + clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect), + clientHttpRequestFactorySettings.getIfAvailable(ClientHttpRequestFactorySettings::defaults), + customizerProvider.orderedStream().toList()); } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java index 56e8b478131..50bc72dde59 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.web.client; +import java.util.Collections; import java.util.List; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; @@ -32,21 +33,21 @@ import org.springframework.web.client.RestClient.Builder; */ public class RestClientBuilderConfigurer { - private ClientHttpRequestFactoryBuilder requestFactoryBuilder; + private final ClientHttpRequestFactoryBuilder requestFactoryBuilder; - private ClientHttpRequestFactorySettings requestFactorySettings; + private final ClientHttpRequestFactorySettings requestFactorySettings; - private List customizers; + private final List customizers; - void setRequestFactoryBuilder(ClientHttpRequestFactoryBuilder requestFactoryBuilder) { + public RestClientBuilderConfigurer() { + this(ClientHttpRequestFactoryBuilder.detect(), ClientHttpRequestFactorySettings.defaults(), + Collections.emptyList()); + } + + RestClientBuilderConfigurer(ClientHttpRequestFactoryBuilder requestFactoryBuilder, + ClientHttpRequestFactorySettings requestFactorySettings, List customizers) { this.requestFactoryBuilder = requestFactoryBuilder; - } - - void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) { this.requestFactorySettings = requestFactorySettings; - } - - void setRestClientCustomizers(List customizers) { this.customizers = customizers; } @@ -57,18 +58,14 @@ public class RestClientBuilderConfigurer { * @return the configured builder */ public RestClient.Builder configure(RestClient.Builder builder) { - ClientHttpRequestFactoryBuilder requestFactoryBuilder = (this.requestFactoryBuilder != null) - ? this.requestFactoryBuilder : ClientHttpRequestFactoryBuilder.detect(); - builder.requestFactory(requestFactoryBuilder.build(this.requestFactorySettings)); + builder.requestFactory(this.requestFactoryBuilder.build(this.requestFactorySettings)); applyCustomizers(builder); return builder; } private void applyCustomizers(Builder builder) { - if (this.customizers != null) { - for (RestClientCustomizer customizer : this.customizers) { - customizer.customize(builder); - } + for (RestClientCustomizer customizer : this.customizers) { + customizer.customize(builder); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java index b5bb408f599..fae799d96a9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -24,6 +24,9 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects; import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.web.client.RestClientCustomizer; @@ -171,6 +174,57 @@ class RestClientAutoConfigurationTests { }); } + @Test + void shouldSupplyRestClientBuilderConfigurerWithCustomSettings() { + ClientHttpRequestFactorySettings clientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults() + .withRedirects(Redirects.DONT_FOLLOW); + ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder = mock( + ClientHttpRequestFactoryBuilder.class); + RestClientCustomizer customizer1 = mock(RestClientCustomizer.class); + RestClientCustomizer customizer2 = mock(RestClientCustomizer.class); + HttpMessageConvertersRestClientCustomizer httpMessageConverterCustomizer = mock( + HttpMessageConvertersRestClientCustomizer.class); + this.contextRunner.withBean(ClientHttpRequestFactorySettings.class, () -> clientHttpRequestFactorySettings) + .withBean(ClientHttpRequestFactoryBuilder.class, () -> clientHttpRequestFactoryBuilder) + .withBean("customizer1", RestClientCustomizer.class, () -> customizer1) + .withBean("customizer2", RestClientCustomizer.class, () -> customizer2) + .withBean("httpMessageConverterCustomizer", HttpMessageConvertersRestClientCustomizer.class, + () -> httpMessageConverterCustomizer) + .run((context) -> { + assertThat(context).hasSingleBean(RestClientBuilderConfigurer.class) + .hasSingleBean(ClientHttpRequestFactorySettings.class) + .hasSingleBean(ClientHttpRequestFactoryBuilder.class); + RestClientBuilderConfigurer configurer = context.getBean(RestClientBuilderConfigurer.class); + assertThat(configurer).hasFieldOrPropertyWithValue("requestFactoryBuilder", + clientHttpRequestFactoryBuilder); + assertThat(configurer).hasFieldOrPropertyWithValue("requestFactorySettings", + clientHttpRequestFactorySettings); + assertThat(configurer).hasFieldOrPropertyWithValue("customizers", + List.of(customizer1, customizer2, httpMessageConverterCustomizer)); + }); + } + + @Test + void shouldSupplyRestClientBuilderConfigurerWithAutoConfiguredHttpSettings() { + RestClientCustomizer customizer1 = mock(RestClientCustomizer.class); + RestClientCustomizer customizer2 = mock(RestClientCustomizer.class); + this.contextRunner.withBean("customizer1", RestClientCustomizer.class, () -> customizer1) + .withBean("customizer2", RestClientCustomizer.class, () -> customizer2) + .run((context) -> { + assertThat(context).hasSingleBean(RestClientBuilderConfigurer.class) + .hasSingleBean(ClientHttpRequestFactorySettings.class) + .hasSingleBean(ClientHttpRequestFactoryBuilder.class) + .hasSingleBean(HttpMessageConvertersRestClientCustomizer.class); + RestClientBuilderConfigurer configurer = context.getBean(RestClientBuilderConfigurer.class); + assertThat(configurer).hasFieldOrPropertyWithValue("requestFactoryBuilder", + context.getBean(ClientHttpRequestFactoryBuilder.class)); + assertThat(configurer).hasFieldOrPropertyWithValue("requestFactorySettings", + context.getBean(ClientHttpRequestFactorySettings.class)); + assertThat(configurer).hasFieldOrPropertyWithValue("customizers", List.of(customizer1, customizer2, + context.getBean(HttpMessageConvertersRestClientCustomizer.class))); + }); + } + @Configuration(proxyBeanMethods = false) static class CodecConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurerTests.java index c4c8395c217..4154e5e6232 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2025 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. @@ -19,11 +19,19 @@ package org.springframework.boot.autoconfigure.web.client; import java.util.List; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; +import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.web.client.RestClientCustomizer; +import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.web.client.RestClient; -import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.mock; @@ -32,23 +40,29 @@ import static org.mockito.Mockito.mock; * * @author Moritz Halbritter */ +@ExtendWith(MockitoExtension.class) class RestClientBuilderConfigurerTests { - @Test - void shouldApplyCustomizers() { - RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); - RestClientCustomizer customizer = mock(RestClientCustomizer.class); - configurer.setRestClientCustomizers(List.of(customizer)); - RestClient.Builder builder = RestClient.builder(); - configurer.configure(builder); - then(customizer).should().customize(builder); - } + @Mock + private ClientHttpRequestFactoryBuilder clientHttpRequestFactoryBuilder; + + @Mock + private ClientHttpRequestFactory clientHttpRequestFactory; @Test - void shouldSupportNullAsCustomizers() { - RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); - configurer.setRestClientCustomizers(null); - assertThatCode(() -> configurer.configure(RestClient.builder())).doesNotThrowAnyException(); + void shouldConfigureRestClientBuilder() { + ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(mock(SslBundle.class)); + RestClientCustomizer customizer = mock(RestClientCustomizer.class); + RestClientCustomizer customizer1 = mock(RestClientCustomizer.class); + RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(this.clientHttpRequestFactoryBuilder, + settings, List.of(customizer, customizer1)); + given(this.clientHttpRequestFactoryBuilder.build(settings)).willReturn(this.clientHttpRequestFactory); + + RestClient.Builder builder = RestClient.builder(); + configurer.configure(builder); + assertThat(builder.build()).hasFieldOrPropertyWithValue("clientRequestFactory", this.clientHttpRequestFactory); + then(customizer).should().customize(builder); + then(customizer1).should().customize(builder); } }