Allow ExchangeStrategies customizations in WebClient
Backport ofd4209392andacfeb77dCloses gh-23961
This commit is contained in:
committed by
Rossen Stoyanchev
parent
59165dd526
commit
83683a13bb
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -42,13 +42,18 @@ final class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Build
|
||||
}
|
||||
|
||||
|
||||
private final ClientCodecConfigurer codecConfigurer = ClientCodecConfigurer.create();
|
||||
private final ClientCodecConfigurer codecConfigurer;
|
||||
|
||||
|
||||
public DefaultExchangeStrategiesBuilder() {
|
||||
this.codecConfigurer = ClientCodecConfigurer.create();
|
||||
this.codecConfigurer.registerDefaults(false);
|
||||
}
|
||||
|
||||
private DefaultExchangeStrategiesBuilder(DefaultExchangeStrategies other) {
|
||||
this.codecConfigurer = other.codecConfigurer.clone();
|
||||
}
|
||||
|
||||
|
||||
public void defaultConfiguration() {
|
||||
this.codecConfigurer.registerDefaults(true);
|
||||
@@ -62,21 +67,23 @@ final class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Build
|
||||
|
||||
@Override
|
||||
public ExchangeStrategies build() {
|
||||
return new DefaultExchangeStrategies(
|
||||
this.codecConfigurer.getReaders(), this.codecConfigurer.getWriters());
|
||||
return new DefaultExchangeStrategies(this.codecConfigurer);
|
||||
}
|
||||
|
||||
|
||||
private static class DefaultExchangeStrategies implements ExchangeStrategies {
|
||||
|
||||
private final ClientCodecConfigurer codecConfigurer;
|
||||
|
||||
private final List<HttpMessageReader<?>> readers;
|
||||
|
||||
private final List<HttpMessageWriter<?>> writers;
|
||||
|
||||
|
||||
public DefaultExchangeStrategies(List<HttpMessageReader<?>> readers, List<HttpMessageWriter<?>> writers) {
|
||||
this.readers = unmodifiableCopy(readers);
|
||||
this.writers = unmodifiableCopy(writers);
|
||||
public DefaultExchangeStrategies(ClientCodecConfigurer codecConfigurer) {
|
||||
this.codecConfigurer = codecConfigurer;
|
||||
this.readers = unmodifiableCopy(this.codecConfigurer.getReaders());
|
||||
this.writers = unmodifiableCopy(this.codecConfigurer.getWriters());
|
||||
}
|
||||
|
||||
private static <T> List<T> unmodifiableCopy(List<? extends T> list) {
|
||||
@@ -93,6 +100,11 @@ final class DefaultExchangeStrategiesBuilder implements ExchangeStrategies.Build
|
||||
public List<HttpMessageWriter<?>> messageWriters() {
|
||||
return this.writers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder mutate() {
|
||||
return new DefaultExchangeStrategiesBuilder(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.JettyClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -38,10 +40,22 @@ import org.springframework.web.util.UriBuilderFactory;
|
||||
* Default implementation of {@link WebClient.Builder}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
*/
|
||||
final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
private static final boolean reactorClientPresent;
|
||||
|
||||
private static final boolean jettyClientPresent;
|
||||
|
||||
static {
|
||||
ClassLoader loader = DefaultWebClientBuilder.class.getClassLoader();
|
||||
reactorClientPresent = ClassUtils.isPresent("reactor.netty.http.client.HttpClient", loader);
|
||||
jettyClientPresent = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private String baseUrl;
|
||||
|
||||
@@ -66,14 +80,17 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
@Nullable
|
||||
private ClientHttpConnector connector;
|
||||
|
||||
private ExchangeStrategies exchangeStrategies;
|
||||
@Nullable
|
||||
private ExchangeStrategies strategies;
|
||||
|
||||
@Nullable
|
||||
private List<Consumer<ExchangeStrategies.Builder>> strategiesConfigurers;
|
||||
|
||||
@Nullable
|
||||
private ExchangeFunction exchangeFunction;
|
||||
|
||||
|
||||
public DefaultWebClientBuilder() {
|
||||
this.exchangeStrategies = ExchangeStrategies.withDefaults();
|
||||
}
|
||||
|
||||
public DefaultWebClientBuilder(DefaultWebClientBuilder other) {
|
||||
@@ -95,7 +112,7 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
this.defaultRequest = other.defaultRequest;
|
||||
this.filters = other.filters != null ? new ArrayList<>(other.filters) : null;
|
||||
this.connector = other.connector;
|
||||
this.exchangeStrategies = other.exchangeStrategies;
|
||||
this.strategies = other.strategies;
|
||||
this.exchangeFunction = other.exchangeFunction;
|
||||
}
|
||||
|
||||
@@ -191,8 +208,16 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
@Override
|
||||
public WebClient.Builder exchangeStrategies(ExchangeStrategies strategies) {
|
||||
Assert.notNull(strategies, "ExchangeStrategies must not be null");
|
||||
this.exchangeStrategies = strategies;
|
||||
this.strategies = strategies;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient.Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer) {
|
||||
if (this.strategiesConfigurers == null) {
|
||||
this.strategiesConfigurers = new ArrayList<>(4);
|
||||
}
|
||||
this.strategiesConfigurers.add(configurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -215,7 +240,9 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
|
||||
@Override
|
||||
public WebClient build() {
|
||||
ExchangeFunction exchange = initExchangeFunction();
|
||||
ExchangeFunction exchange = (this.exchangeFunction == null ?
|
||||
ExchangeFunctions.create(getOrInitConnector(), initExchangeStrategies()) :
|
||||
this.exchangeFunction);
|
||||
ExchangeFunction filteredExchange = (this.filters != null ? this.filters.stream()
|
||||
.reduce(ExchangeFilterFunction::andThen)
|
||||
.map(filter -> filter.apply(exchange))
|
||||
@@ -226,16 +253,29 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
|
||||
this.defaultRequest, new DefaultWebClientBuilder(this));
|
||||
}
|
||||
|
||||
private ExchangeFunction initExchangeFunction() {
|
||||
if (this.exchangeFunction != null) {
|
||||
return this.exchangeFunction;
|
||||
private ClientHttpConnector getOrInitConnector() {
|
||||
if (this.connector != null) {
|
||||
return this.connector;
|
||||
}
|
||||
else if (this.connector != null) {
|
||||
return ExchangeFunctions.create(this.connector, this.exchangeStrategies);
|
||||
else if (reactorClientPresent) {
|
||||
return new ReactorClientHttpConnector();
|
||||
}
|
||||
else {
|
||||
return ExchangeFunctions.create(new ReactorClientHttpConnector(), this.exchangeStrategies);
|
||||
else if (jettyClientPresent) {
|
||||
return new JettyClientHttpConnector();
|
||||
}
|
||||
throw new IllegalStateException("No suitable default ClientHttpConnector found");
|
||||
}
|
||||
|
||||
private ExchangeStrategies initExchangeStrategies() {
|
||||
if (CollectionUtils.isEmpty(this.strategiesConfigurers)) {
|
||||
return this.strategies != null ? this.strategies : ExchangeStrategies.withDefaults();
|
||||
}
|
||||
|
||||
ExchangeStrategies.Builder builder =
|
||||
this.strategies != null ? this.strategies.mutate() : ExchangeStrategies.builder();
|
||||
|
||||
this.strategiesConfigurers.forEach(configurer -> configurer.accept(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private UriBuilderFactory initUriBuilderFactory() {
|
||||
|
||||
@@ -47,6 +47,15 @@ public interface ExchangeStrategies {
|
||||
*/
|
||||
List<HttpMessageWriter<?>> messageWriters();
|
||||
|
||||
/**
|
||||
* Return a builder to create a new {@link ExchangeStrategies} instance
|
||||
* replicated from the current instance.
|
||||
* @since 5.1.12
|
||||
*/
|
||||
default Builder mutate() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
// Static builder methods
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ import org.springframework.web.util.UriBuilderFactory;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Arjen Poutsma
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface WebClient {
|
||||
@@ -289,11 +290,25 @@ public interface WebClient {
|
||||
|
||||
/**
|
||||
* Configure the {@link ExchangeStrategies} to use.
|
||||
* <p>By default this is obtained from {@link ExchangeStrategies#withDefaults()}.
|
||||
* <p>Note that in a scenario where the builder is configured by
|
||||
* multiple parties, it is preferable to use
|
||||
* {@link #exchangeStrategies(Consumer)} in order to customize the same
|
||||
* {@code ExchangeStrategies}. This method here sets the strategies that
|
||||
* everyone else then can customize.
|
||||
* <p>By default this is {@link ExchangeStrategies#withDefaults()}.
|
||||
* @param strategies the strategies to use
|
||||
*/
|
||||
Builder exchangeStrategies(ExchangeStrategies strategies);
|
||||
|
||||
/**
|
||||
* Customize the strategies configured via
|
||||
* {@link #exchangeStrategies(ExchangeStrategies)}. This method is
|
||||
* designed for use in scenarios where multiple parties wish to update
|
||||
* the {@code ExchangeStrategies}.
|
||||
* @since 5.1.12
|
||||
*/
|
||||
Builder exchangeStrategies(Consumer<ExchangeStrategies.Builder> configurer);
|
||||
|
||||
/**
|
||||
* Provide an {@link ExchangeFunction} pre-configured with
|
||||
* {@link ClientHttpConnector} and {@link ExchangeStrategies}.
|
||||
|
||||
@@ -39,4 +39,16 @@ public class ExchangeStrategiesTests {
|
||||
assertFalse(strategies.messageWriters().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
public void mutate() {
|
||||
ExchangeStrategies strategies = ExchangeStrategies.empty().build();
|
||||
assertTrue(strategies.messageReaders().isEmpty());
|
||||
assertTrue(strategies.messageWriters().isEmpty());
|
||||
|
||||
ExchangeStrategies mutated = strategies.mutate().codecs(codecs -> codecs.registerDefaults(true)).build();
|
||||
assertFalse(mutated.messageReaders().isEmpty());
|
||||
assertFalse(mutated.messageWriters().isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user