Make ZipkinHttpClientSender the default BytesMessageSender

Switch `HttpClientSenderConfiguration` and `UrlConnectionSenderConfiguration`
import order to set default sender.

See gh-43085
This commit is contained in:
YiXuan Ding 2024-11-08 00:31:35 +08:00 committed by Phillip Webb
parent e9abe34635
commit 2b60c631a9
2 changed files with 81 additions and 76 deletions

View File

@ -52,37 +52,60 @@ import org.springframework.web.reactive.function.client.WebClient;
* *
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Stefan Bratanov * @author Stefan Bratanov
* @author Wick Dynex
*/ */
class ZipkinConfigurations { class ZipkinConfigurations {
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@Import({ UrlConnectionSenderConfiguration.class, WebClientSenderConfiguration.class, @Import({ HttpClientSenderConfiguration.class, WebClientSenderConfiguration.class,
RestTemplateSenderConfiguration.class, HttpClientSenderConfiguration.class }) RestTemplateSenderConfiguration.class, UrlConnectionSenderConfiguration.class })
static class SenderConfiguration { static class SenderConfiguration {
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(URLConnectionSender.class) @ConditionalOnClass(HttpClient.class)
@EnableConfigurationProperties(ZipkinProperties.class) @EnableConfigurationProperties(ZipkinProperties.class)
static class UrlConnectionSenderConfiguration { static class HttpClientSenderConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(BytesMessageSender.class) @ConditionalOnMissingBean(BytesMessageSender.class)
URLConnectionSender urlConnectionSender(ZipkinProperties properties, Encoding encoding, ZipkinHttpClientSender httpClientSender(ZipkinProperties properties, Encoding encoding,
ObjectProvider<ZipkinHttpClientBuilderCustomizer> customizers,
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider, ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) { ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) {
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties)); .getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider
.getIfAvailable(HttpEndpointSuppliers::constantFactory); .getIfAvailable(HttpEndpointSuppliers::constantFactory);
URLConnectionSender.Builder builder = URLConnectionSender.newBuilder(); Builder httpClientBuilder = HttpClient.newBuilder().connectTimeout(properties.getConnectTimeout());
builder.connectTimeout((int) properties.getConnectTimeout().toMillis()); customizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
builder.readTimeout((int) properties.getReadTimeout().toMillis()); return new ZipkinHttpClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(),
builder.endpointSupplierFactory(endpointSupplierFactory); httpClientBuilder.build(), properties.getReadTimeout());
builder.endpoint(connectionDetails.getSpanEndpoint()); }
builder.encoding(encoding);
return builder.build(); }
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebClient.class)
@EnableConfigurationProperties(ZipkinProperties.class)
static class WebClientSenderConfiguration {
@Bean
@ConditionalOnMissingBean(BytesMessageSender.class)
@SuppressWarnings({ "deprecation", "removal" })
ZipkinWebClientSender webClientSender(ZipkinProperties properties, Encoding encoding,
ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers,
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) {
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider
.getIfAvailable(HttpEndpointSuppliers::constantFactory);
WebClient.Builder builder = WebClient.builder();
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return new ZipkinWebClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(),
builder.build(), properties.getConnectTimeout().plus(properties.getReadTimeout()));
} }
} }
@ -126,48 +149,26 @@ class ZipkinConfigurations {
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebClient.class) @ConditionalOnClass(URLConnectionSender.class)
@EnableConfigurationProperties(ZipkinProperties.class) @EnableConfigurationProperties(ZipkinProperties.class)
static class WebClientSenderConfiguration { static class UrlConnectionSenderConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(BytesMessageSender.class) @ConditionalOnMissingBean(BytesMessageSender.class)
@SuppressWarnings({ "deprecation", "removal" }) URLConnectionSender urlConnectionSender(ZipkinProperties properties, Encoding encoding,
ZipkinWebClientSender webClientSender(ZipkinProperties properties, Encoding encoding,
ObjectProvider<ZipkinWebClientBuilderCustomizer> customizers,
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider, ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) { ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) {
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties)); .getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider
.getIfAvailable(HttpEndpointSuppliers::constantFactory); .getIfAvailable(HttpEndpointSuppliers::constantFactory);
WebClient.Builder builder = WebClient.builder(); URLConnectionSender.Builder builder = URLConnectionSender.newBuilder();
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); builder.connectTimeout((int) properties.getConnectTimeout().toMillis());
return new ZipkinWebClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(), builder.readTimeout((int) properties.getReadTimeout().toMillis());
builder.build(), properties.getConnectTimeout().plus(properties.getReadTimeout())); builder.endpointSupplierFactory(endpointSupplierFactory);
} builder.endpoint(connectionDetails.getSpanEndpoint());
builder.encoding(encoding);
} return builder.build();
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpClient.class)
@EnableConfigurationProperties(ZipkinProperties.class)
static class HttpClientSenderConfiguration {
@Bean
@ConditionalOnMissingBean(BytesMessageSender.class)
ZipkinHttpClientSender httpClientSender(ZipkinProperties properties, Encoding encoding,
ObjectProvider<ZipkinHttpClientBuilderCustomizer> customizers,
ObjectProvider<ZipkinConnectionDetails> connectionDetailsProvider,
ObjectProvider<HttpEndpointSupplier.Factory> endpointSupplierFactoryProvider) {
ZipkinConnectionDetails connectionDetails = connectionDetailsProvider
.getIfAvailable(() -> new PropertiesZipkinConnectionDetails(properties));
HttpEndpointSupplier.Factory endpointSupplierFactory = endpointSupplierFactoryProvider
.getIfAvailable(HttpEndpointSuppliers::constantFactory);
Builder httpClientBuilder = HttpClient.newBuilder().connectTimeout(properties.getConnectTimeout());
customizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
return new ZipkinHttpClientSender(encoding, endpointSupplierFactory, connectionDetails.getSpanEndpoint(),
httpClientBuilder.build(), properties.getReadTimeout());
} }
} }

View File

@ -17,6 +17,7 @@
package org.springframework.boot.actuate.autoconfigure.tracing.zipkin; package org.springframework.boot.actuate.autoconfigure.tracing.zipkin;
import java.io.IOException; import java.io.IOException;
import java.net.http.HttpClient;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -31,6 +32,7 @@ import zipkin2.reporter.HttpEndpointSupplier;
import zipkin2.reporter.urlconnection.URLConnectionSender; import zipkin2.reporter.urlconnection.URLConnectionSender;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.SenderConfiguration; import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.SenderConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinConfigurations.UrlConnectionSenderConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -39,6 +41,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -49,6 +52,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link SenderConfiguration}. * Tests for {@link SenderConfiguration}.
* *
* @author Moritz Halbritter * @author Moritz Halbritter
* @author Wick Dynex
*/ */
@SuppressWarnings({ "deprecation", "removal" }) @SuppressWarnings({ "deprecation", "removal" })
class ZipkinConfigurationsSenderConfigurationTests { class ZipkinConfigurationsSenderConfigurationTests {
@ -63,34 +67,33 @@ class ZipkinConfigurationsSenderConfigurationTests {
.withConfiguration(AutoConfigurations.of(DefaultEncodingConfiguration.class, SenderConfiguration.class)); .withConfiguration(AutoConfigurations.of(DefaultEncodingConfiguration.class, SenderConfiguration.class));
@Test @Test
void shouldSupplyBeans() { void shouldSupplyDefaultHttpClientSenderBeans() {
this.contextRunner.run((context) -> { this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(URLConnectionSender.class); assertThat(context).hasSingleBean(ZipkinHttpClientSender.class);
assertThat(context).doesNotHaveBean(ZipkinRestTemplateSender.class); assertThat(context).doesNotHaveBean(ZipkinRestTemplateSender.class);
assertThat(context).doesNotHaveBean(ZipkinWebClientSenderTests.class);
assertThat(context).doesNotHaveBean(URLConnectionSender.class);
}); });
} }
@Test @Test
void shouldUseHttpClientIfUrlSenderIsNotAvailable() { void shouldUseUrlSenderIfHttpSenderIsNotAvailable() {
this.contextRunner.withUserConfiguration(HttpClientConfiguration.class) this.contextRunner.withUserConfiguration(UrlConnectionSenderConfiguration.class)
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection", "org.springframework.web.client", .withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class, RestTemplate.class))
"org.springframework.web.reactive.function.client"))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinHttpClientSender.class); assertThat(context).hasSingleBean(URLConnectionSender.class);
then(context.getBean(ZipkinHttpClientBuilderCustomizer.class)).should()
.customize(ArgumentMatchers.any());
}); });
} }
@Test @Test
void shouldPreferWebClientSenderIfWebApplicationIsReactiveAndUrlSenderIsNotAvailable() { void shouldPreferWebClientSenderIfWebApplicationIsReactiveAndHttpClientSenderIsNotAvailable() {
this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class) this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection")) .withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinWebClientSender.class); assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
then(context.getBean(ZipkinWebClientBuilderCustomizer.class)).should() then(context.getBean(ZipkinWebClientBuilderCustomizer.class)).should()
@ -99,55 +102,55 @@ class ZipkinConfigurationsSenderConfigurationTests {
} }
@Test @Test
void shouldPreferWebClientSenderIfWebApplicationIsServletAndUrlSenderIsNotAvailable() { void shouldPreferWebClientSenderIfWebApplicationIsServletAndHttpClientSenderIsNotAvailable() {
this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class) this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection")) .withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinWebClientSender.class); assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
}); });
} }
@Test @Test
void shouldPreferWebClientInNonWebApplicationAndUrlConnectionSenderIsNotAvailable() { void shouldPreferWebClientInNonWebApplicationAndHttpClientSenderIsNotAvailable() {
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class) this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class, WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader("zipkin2.reporter.urlconnection")) .withClassLoader(new FilteredClassLoader(HttpClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinWebClientSender.class); assertThat(context).hasSingleBean(ZipkinWebClientSender.class);
}); });
} }
@Test @Test
void willUseRestTemplateInNonWebApplicationIfUrlConnectionSenderAndWebClientAreNotAvailable() { void willUseRestTemplateInNonWebApplicationIfSenderAndWebClientAreNotAvailable() {
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class) this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)) .withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(HttpClient.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class); assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
}); });
} }
@Test @Test
void willUseRestTemplateInServletWebApplicationIfUrlConnectionSenderAndWebClientNotAvailable() { void willUseRestTemplateInServletWebApplicationIfHttpClientSenderAndWebClientNotAvailable() {
this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class) this.servletContextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)) .withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class); assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
}); });
} }
@Test @Test
void willUseRestTemplateInReactiveWebApplicationIfUrlConnectionSenderAndWebClientAreNotAvailable() { void willUseRestTemplateInReactiveWebApplicationIfHttpClientSenderAndWebClientAreNotAvailable() {
this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class) this.reactiveContextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)) .withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).doesNotHaveBean(URLConnectionSender.class); assertThat(context).doesNotHaveBean(ZipkinHttpClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class); assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
}); });
@ -158,7 +161,7 @@ class ZipkinConfigurationsSenderConfigurationTests {
this.reactiveContextRunner.run((context) -> { this.reactiveContextRunner.run((context) -> {
assertThat(context).doesNotHaveBean(ZipkinWebClientSender.class); assertThat(context).doesNotHaveBean(ZipkinWebClientSender.class);
assertThat(context).hasSingleBean(BytesMessageSender.class); assertThat(context).hasSingleBean(BytesMessageSender.class);
assertThat(context).hasSingleBean(URLConnectionSender.class); assertThat(context).hasSingleBean(ZipkinHttpClientSender.class);
}); });
} }
@ -177,7 +180,7 @@ class ZipkinConfigurationsSenderConfigurationTests {
this.reactiveContextRunner this.reactiveContextRunner
.withPropertyValues("management.zipkin.tracing.endpoint=" + mockWebServer.url("/")) .withPropertyValues("management.zipkin.tracing.endpoint=" + mockWebServer.url("/"))
.withUserConfiguration(RestTemplateConfiguration.class) .withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)) .withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class); assertThat(context).hasSingleBean(ZipkinRestTemplateSender.class);
ZipkinRestTemplateSender sender = context.getBean(ZipkinRestTemplateSender.class); ZipkinRestTemplateSender sender = context.getBean(ZipkinRestTemplateSender.class);
@ -192,6 +195,7 @@ class ZipkinConfigurationsSenderConfigurationTests {
@Test @Test
void shouldUseCustomHttpEndpointSupplierFactory() { void shouldUseCustomHttpEndpointSupplierFactory() {
this.contextRunner.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class) this.contextRunner.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class)
.withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class, RestTemplate.class))
.run((context) -> assertThat(context.getBean(URLConnectionSender.class)) .run((context) -> assertThat(context.getBean(URLConnectionSender.class))
.extracting("delegate.endpointSupplier") .extracting("delegate.endpointSupplier")
.isInstanceOf(CustomHttpEndpointSupplier.class)); .isInstanceOf(CustomHttpEndpointSupplier.class));
@ -201,7 +205,7 @@ class ZipkinConfigurationsSenderConfigurationTests {
@SuppressWarnings("resource") @SuppressWarnings("resource")
void shouldUseCustomHttpEndpointSupplierFactoryWhenReactive() { void shouldUseCustomHttpEndpointSupplierFactoryWhenReactive() {
this.reactiveContextRunner.withUserConfiguration(WebClientConfiguration.class) this.reactiveContextRunner.withUserConfiguration(WebClientConfiguration.class)
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class)) .withClassLoader(new FilteredClassLoader(HttpClient.class))
.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class) .withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class)
.run((context) -> assertThat(context.getBean(ZipkinWebClientSender.class)).extracting("endpointSupplier") .run((context) -> assertThat(context.getBean(ZipkinWebClientSender.class)).extracting("endpointSupplier")
.isInstanceOf(CustomHttpEndpointSupplier.class)); .isInstanceOf(CustomHttpEndpointSupplier.class));
@ -211,7 +215,7 @@ class ZipkinConfigurationsSenderConfigurationTests {
@SuppressWarnings("resource") @SuppressWarnings("resource")
void shouldUseCustomHttpEndpointSupplierFactoryWhenRestTemplate() { void shouldUseCustomHttpEndpointSupplierFactoryWhenRestTemplate() {
this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class) this.contextRunner.withUserConfiguration(RestTemplateConfiguration.class)
.withClassLoader(new FilteredClassLoader(URLConnectionSender.class, WebClient.class)) .withClassLoader(new FilteredClassLoader(HttpClient.class, WebClient.class))
.withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class) .withUserConfiguration(CustomHttpEndpointSupplierFactoryConfiguration.class)
.run((context) -> assertThat(context.getBean(ZipkinRestTemplateSender.class)).extracting("endpointSupplier") .run((context) -> assertThat(context.getBean(ZipkinRestTemplateSender.class)).extracting("endpointSupplier")
.isInstanceOf(CustomHttpEndpointSupplier.class)); .isInstanceOf(CustomHttpEndpointSupplier.class));