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");
* you may not use this file except in compliance with the License.
@ -79,11 +79,10 @@ public class RestClientAutoConfiguration {
ObjectProvider<ClientHttpRequestFactoryBuilder<?>> clientHttpRequestFactoryBuilder,
ObjectProvider<ClientHttpRequestFactorySettings> clientHttpRequestFactorySettings,
ObjectProvider<RestClientCustomizer> 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

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");
* 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<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;
}
void setRequestFactorySettings(ClientHttpRequestFactorySettings requestFactorySettings) {
this.requestFactorySettings = requestFactorySettings;
}
void setRestClientCustomizers(List<RestClientCustomizer> 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);
}
}

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");
* 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 {

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");
* 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<ClientHttpRequestFactory> 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);
}
}