Merge branch '3.4.x'

Closes gh-45016
This commit is contained in:
Moritz Halbritter 2025-04-07 10:58:19 +02:00
commit e39ce56707
4 changed files with 104 additions and 40 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -79,11 +79,10 @@ public class RestClientAutoConfiguration {
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder, ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings, ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
ObjectProvider<RestClientCustomizer> customizerProvider) { ObjectProvider<RestClientCustomizer> customizerProvider) {
RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); return new RestClientBuilderConfigurer(
configurer.setRequestFactoryBuilder(clientHttpRequestFactoryBuilder.getIfAvailable()); clientHttpRequestFactoryBuilder.getIfAvailable(ClientHttpRequestFactoryBuilder::detect),
configurer.setRequestFactorySettings(clientHttpRequestFactorySettings.getIfAvailable()); clientHttpRequestFactorySettings.getIfAvailable(ClientHttpRequestFactorySettings::defaults),
configurer.setRestClientCustomizers(customizerProvider.orderedStream().toList()); customizerProvider.orderedStream().toList());
return configurer;
} }
@Bean @Bean

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; package org.springframework.boot.autoconfigure.web.client;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
@ -32,21 +33,21 @@ import org.springframework.web.client.RestClient.Builder;
*/ */
public class RestClientBuilderConfigurer { public class RestClientBuilderConfigurer {
private ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder; private final ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder;
private ClientHttpRequestFactorySettings requestFactorySettings; private final ClientHttpRequestFactorySettings requestFactorySettings;
private List<RestClientCustomizer> customizers; private final List<RestClientCustomizer> customizers;
void setRequestFactoryBuilder(ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder) { public RestClientBuilderConfigurer() {
this(ClientHttpRequestFactoryBuilder.detect(), ClientHttpRequestFactorySettings.defaults(),
Collections.emptyList());
}
RestClientBuilderConfigurer(ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder,
ClientHttpRequestFactorySettings requestFactorySettings, List<RestClientCustomizer> customizers) {
this.requestFactoryBuilder = requestFactoryBuilder; this.requestFactoryBuilder = requestFactoryBuilder;
}
void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) {
this.requestFactorySettings = requestFactorySettings; this.requestFactorySettings = requestFactorySettings;
}
void setRestClientCustomizers(List<RestClientCustomizer> customizers) {
this.customizers = customizers; this.customizers = customizers;
} }
@ -57,19 +58,15 @@ public class RestClientBuilderConfigurer {
* @return the configured builder * @return the configured builder
*/ */
public RestClient.Builder configure(RestClient.Builder builder) { public RestClient.Builder configure(RestClient.Builder builder) {
ClientHttpRequestFactoryBuilder<?> requestFactoryBuilder = (this.requestFactoryBuilder != null) builder.requestFactory(this.requestFactoryBuilder.build(this.requestFactorySettings));
? this.requestFactoryBuilder : ClientHttpRequestFactoryBuilder.detect();
builder.requestFactory(requestFactoryBuilder.build(this.requestFactorySettings));
applyCustomizers(builder); applyCustomizers(builder);
return builder; return builder;
} }
private void applyCustomizers(Builder builder) { private void applyCustomizers(Builder builder) {
if (this.customizers != null) {
for (RestClientCustomizer customizer : this.customizers) { for (RestClientCustomizer customizer : this.customizers) {
customizer.customize(builder); customizer.customize(builder);
} }
} }
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.HttpMessageConverters;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.http.client.HttpClientAutoConfiguration; 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.ssl.SslBundles;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.web.client.RestClientCustomizer; 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) @Configuration(proxyBeanMethods = false)
static class CodecConfiguration { static class CodecConfiguration {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 java.util.List;
import org.junit.jupiter.api.Test; 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.boot.web.client.RestClientCustomizer;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.web.client.RestClient; 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.BDDMockito.then;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -32,23 +40,29 @@ import static org.mockito.Mockito.mock;
* *
* @author Moritz Halbritter * @author Moritz Halbritter
*/ */
@ExtendWith(MockitoExtension.class)
class RestClientBuilderConfigurerTests { class RestClientBuilderConfigurerTests {
@Mock
private ClientHttpRequestFactoryBuilder<ClientHttpRequestFactory> clientHttpRequestFactoryBuilder;
@Mock
private ClientHttpRequestFactory clientHttpRequestFactory;
@Test @Test
void shouldApplyCustomizers() { void shouldConfigureRestClientBuilder() {
RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer(); ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.ofSslBundle(mock(SslBundle.class));
RestClientCustomizer customizer = mock(RestClientCustomizer.class); RestClientCustomizer customizer = mock(RestClientCustomizer.class);
configurer.setRestClientCustomizers(List.of(customizer)); 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(); RestClient.Builder builder = RestClient.builder();
configurer.configure(builder); configurer.configure(builder);
assertThat(builder.build()).hasFieldOrPropertyWithValue("clientRequestFactory", this.clientHttpRequestFactory);
then(customizer).should().customize(builder); then(customizer).should().customize(builder);
} then(customizer1).should().customize(builder);
@Test
void shouldSupportNullAsCustomizers() {
RestClientBuilderConfigurer configurer = new RestClientBuilderConfigurer();
configurer.setRestClientCustomizers(null);
assertThatCode(() -> configurer.configure(RestClient.builder())).doesNotThrowAnyException();
} }
} }