From e7e391a65aa30b7aec8a78b2a0aafee9544eaa6e Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Mon, 15 Oct 2018 09:11:16 -0400 Subject: [PATCH 1/5] Adds event that will be fired before a service is registered. Fixes #302 (#424) --- .../main/asciidoc/spring-cloud-commons.adoc | 9 ++++ .../event/InstancePreRegisteredEvent.java | 47 +++++++++++++++++ .../AbstractAutoServiceRegistration.java | 2 + .../AbstractAutoServiceRegistrationTests.java | 50 ++++++++++++++++++- 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/event/InstancePreRegisteredEvent.java diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc index 36e576aa..440af55b 100644 --- a/docs/src/main/asciidoc/spring-cloud-commons.adoc +++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc @@ -298,6 +298,15 @@ To disable that behavior, you can set: * `@EnableDiscoveryClient(autoRegister=false)` to permanently disable auto-registration. * `spring.cloud.service-registry.auto-registration.enabled=false` to disable the behavior through configuration. +===== ServiceRegistry Auto-Registration Events + +There are two events that will be fired when a service auto-registers. The first event, called +`InstancePreRegisteredEvent`, is fired before the service is registered. The second +event, called `InstanceRegisteredEvent`, is fired after the service is registered. You can register an +`ApplicationListener`(s) to listen to and react to these events. + +NOTE: These events will not be fired if `spring.cloud.service-registry.auto-registration.enabled` is set to `false`. + ==== Service Registry Actuator Endpoint Spring Cloud Commons provides a `/service-registry` actuator endpoint. diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/event/InstancePreRegisteredEvent.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/event/InstancePreRegisteredEvent.java new file mode 100644 index 00000000..6253c546 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/event/InstancePreRegisteredEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.client.discovery.event; + +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.context.ApplicationEvent; + +/** + * An event to fire before a service is registered. + * @author Ryan Baxter + */ +public class InstancePreRegisteredEvent extends ApplicationEvent { + + private Registration registration; + + /** + * Create a new pre registration event. + * + * @param source the object on which the event initially occurred (never {@code null}) + */ + public InstancePreRegisteredEvent(Object source, Registration registration) { + super(source); + this.registration = registration; + } + + /** + * Get the registration data. + * @return the registration data + */ + public Registration getRegistration() { + return registration; + } +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java index afa67d2d..e3d6a0d5 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistration.java @@ -6,6 +6,7 @@ import org.springframework.beans.BeansException; import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.cloud.client.discovery.ManagementServerPortUtils; +import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent; import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -105,6 +106,7 @@ public abstract class AbstractAutoServiceRegistration // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below if (!this.running.get()) { + this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration())); register(); if (shouldRegisterManagement()) { registerManagement(); diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationTests.java index 701dbace..92d618cd 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/serviceregistry/AbstractAutoServiceRegistrationTests.java @@ -11,8 +11,12 @@ import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagement import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent; +import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; +import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; import org.springframework.test.context.junit4.SpringRunner; import static org.hamcrest.Matchers.instanceOf; @@ -34,6 +38,12 @@ public class AbstractAutoServiceRegistrationTests { @Autowired private TestAutoServiceRegistration autoRegistration; + @Autowired + private PreEventListener preEventListener; + + @Autowired + public PostEventListener postEventListener; + @LocalServerPort private int port; @@ -52,6 +62,14 @@ public class AbstractAutoServiceRegistrationTests { assertEquals("Lifecycle appName is wrong", "application", autoRegistration.getAppName()); } + @Test + public void eventsFireTest() { + assertTrue(preEventListener.wasFired); + assertEquals("testRegistration2", preEventListener.registration.getServiceId()); + assertTrue(postEventListener.wasFired); + assertEquals("testRegistration2", postEventListener.config.getServiceId()); + } + @EnableAutoConfiguration @Configuration public static class Config { @@ -59,6 +77,36 @@ public class AbstractAutoServiceRegistrationTests { public TestAutoServiceRegistration testAutoServiceRegistration() { return new TestAutoServiceRegistration(); } + + @Bean + public PreEventListener preRegisterListener() { + return new PreEventListener(); + } + + @Bean + public PostEventListener postEventListener() { + return new PostEventListener(); + } + } + + public static class PreEventListener implements ApplicationListener { + public boolean wasFired = false; + public Registration registration; + @Override + public void onApplicationEvent(InstancePreRegisteredEvent event) { + this.registration = event.getRegistration(); + this.wasFired = true; + } + } + + public static class PostEventListener implements ApplicationListener { + public boolean wasFired = false; + public Registration config; + @Override + public void onApplicationEvent(InstanceRegisteredEvent event) { + this.config = (Registration)event.getConfig(); + this.wasFired = true; + } } public static class TestRegistration implements Registration { @@ -188,7 +236,7 @@ public class AbstractAutoServiceRegistrationTests { @Override protected Object getConfiguration() { - return null; + return getRegistration(); } @Override From 575fb3141282fdcb2ccc91a32332fa980d200305 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Mon, 15 Oct 2018 11:35:09 -0400 Subject: [PATCH 2/5] Fix documentation for adding a backoff policy. Fixes #423 --- docs/src/main/asciidoc/spring-cloud-commons.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc index 440af55b..69c481d3 100644 --- a/docs/src/main/asciidoc/spring-cloud-commons.adoc +++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc @@ -393,15 +393,15 @@ You can use `client.ribbon.MaxAutoRetries`, `client.ribbon.MaxAutoRetriesNextSer If you would like to disable the retry logic with Spring Retry on the classpath, you can set `spring.cloud.loadbalancer.retry.enabled=false`. See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties[Ribbon documentation] for a description of what these properties do. -If you would like to implement a `BackOffPolicy` in your retries, you need to create a bean of type `LoadBalancedBackOffPolicyFactory` and return the `BackOffPolicy` you would like to use for a given service, as shown in the following example: +If you would like to implement a `BackOffPolicy` in your retries, you need to create a bean of type `LoadBalancedRetryFactory` and override the `createBackOffPolicy` method: [source,java,indent=0] ---- @Configuration public class MyConfiguration { @Bean - LoadBalancedBackOffPolicyFactory backOffPolciyFactory() { - return new LoadBalancedBackOffPolicyFactory() { + LoadBalancedRetryFactory retryFactory() { + return new LoadBalancedRetryFactory() { @Override public BackOffPolicy createBackOffPolicy(String service) { return new ExponentialBackOffPolicy(); From 5248f3fc326efed72d5bd48e6b4cab37b95b1e5b Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 17 Oct 2018 20:11:56 -0400 Subject: [PATCH 3/5] Return a client response from filter function instead of throwing an exception. Fixes #386 (#430) --- .../LoadBalancerExchangeFilterFunction.java | 20 +++++++++++--- ...adBalancerExchangeFilterFunctionTests.java | 26 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunction.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunction.java index b7c30c63..db8f239b 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunction.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunction.java @@ -2,9 +2,11 @@ package org.springframework.cloud.client.loadbalancer.reactive; import java.net.URI; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; -import org.springframework.util.Assert; +import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; @@ -14,9 +16,13 @@ import reactor.core.publisher.Mono; /** * @author Spencer Gibb + * @author Ryan Baxter */ public class LoadBalancerExchangeFilterFunction implements ExchangeFilterFunction { + private static Log logger = LogFactory + .getLog(LoadBalancerExchangeFilterFunction.class); + private final LoadBalancerClient loadBalancerClient; public LoadBalancerExchangeFilterFunction(LoadBalancerClient loadBalancerClient) { @@ -27,10 +33,18 @@ public class LoadBalancerExchangeFilterFunction implements ExchangeFilterFunctio public Mono filter(ClientRequest request, ExchangeFunction next) { URI originalUrl = request.url(); String serviceId = originalUrl.getHost(); - Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUrl); + if(serviceId == null) { + String msg = String.format("Request URI does not contain a valid hostname: %s", originalUrl.toString()); + logger.warn(msg); + return Mono.just(ClientResponse.create(HttpStatus.BAD_REQUEST).body(msg).build()); + } //TODO: reactive lb client - ServiceInstance instance = this.loadBalancerClient.choose(serviceId); + if(instance == null) { + String msg = String.format("Load balancer does not contain an instance for the service %s", serviceId); + logger.warn(msg); + return Mono.just(ClientResponse.create(HttpStatus.SERVICE_UNAVAILABLE).body(msg).build()); + } URI uri = this.loadBalancerClient.reconstructURI(instance, originalUrl); ClientRequest newRequest = ClientRequest.method(request.method(), uri) .headers(headers -> headers.addAll(request.headers())) diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunctionTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunctionTests.java index 2f22f8e9..b45072fa 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunctionTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerExchangeFilterFunctionTests.java @@ -22,9 +22,11 @@ import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperti import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.util.UriComponentsBuilder; @@ -33,6 +35,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen /** * @author Spencer Gibb + * @author Ryan Baxter */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT) @@ -68,6 +71,26 @@ public class LoadBalancerExchangeFilterFunctionTests { assertThat(value).isEqualTo("Hello World"); } + @Test + public void testNoInstance() { + ClientResponse clientResponse = WebClient.builder() + .baseUrl("http://foobar") + .filter(lbFunction) + .build() + .get().exchange().block(); + assertThat(clientResponse.statusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + } + + @Test + public void testNoHostName() { + ClientResponse clientResponse = WebClient.builder() + .baseUrl("http:///foobar") + .filter(lbFunction) + .build() + .get().exchange().block(); + assertThat(clientResponse.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + @EnableDiscoveryClient @EnableAutoConfiguration @SpringBootConfiguration @@ -106,6 +129,9 @@ public class LoadBalancerExchangeFilterFunctionTests { @Override public ServiceInstance choose(String serviceId) { List instances = discoveryClient.getInstances(serviceId); + if(instances.size() == 0) { + return null; + } int instanceIdx = random.nextInt(instances.size()); return instances.get(instanceIdx); } From bbc82b423c4e510726640346752882af844ddff9 Mon Sep 17 00:00:00 2001 From: lxy <391861737@qq.com> Date: Thu, 18 Oct 2018 21:51:59 +0800 Subject: [PATCH 4/5] Set MainApplicationClass if it is null (#426) MainApplicationClass will be null, if it is booted from SpringBootServletInitializer. So we need to set it properly. Fixes gh-425 --- .../bootstrap/BootstrapApplicationListener.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java index 864733d8..e376a3a9 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java @@ -173,13 +173,24 @@ public class BootstrapApplicationListener // Don't use the default properties in this builder .registerShutdownHook(false).logStartupInfo(false) .web(WebApplicationType.NONE); + final SpringApplication builderApplication = builder.application(); + if(builderApplication.getMainApplicationClass() == null){ + // gh_425: + // SpringApplication cannot deduce the MainApplicationClass here + // if it is booted from SpringBootServletInitializer due to the + // absense of the "main" method in stackTraces. + // But luckily this method's second parameter "application" here + // carries the real MainApplicationClass which has been explicitly + // set by SpringBootServletInitializer itself already. + builder.main(application.getMainApplicationClass()); + } if (environment.getPropertySources().contains("refreshArgs")) { // If we are doing a context refresh, really we only want to refresh the // Environment, and there are some toxic listeners (like the // LoggingApplicationListener) that affect global static state, so we need a // way to switch those off. - builder.application() - .setListeners(filterListeners(builder.application().getListeners())); + builderApplication + .setListeners(filterListeners(builderApplication.getListeners())); } List> sources = new ArrayList<>(); for (String name : names) { From ca80e3ac786600db64e7af5aa23d47eb4aaaa7b0 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Mon, 22 Oct 2018 15:59:56 -0400 Subject: [PATCH 5/5] Uses custom WebClientCustomizer rather than one from Boot. Using the boot WebClientCustomizer caused the auto configured WebClient.Builder to use the loadbalancer filter function even though it wasn't annotated with `@LoadBalanced`. Fixes gh-434 --- ...ReactiveLoadBalancerAutoConfiguration.java | 1 - .../reactive/WebClientCustomizer.java | 43 +++++++++++++ ...iveLoadBalancerAutoConfigurationTests.java | 63 +++++++++++-------- 3 files changed, 80 insertions(+), 27 deletions(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/WebClientCustomizer.java diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfiguration.java index 44fd99c8..be28a4a0 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfiguration.java @@ -4,7 +4,6 @@ import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.context.annotation.Bean; diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/WebClientCustomizer.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/WebClientCustomizer.java new file mode 100644 index 00000000..0bcb3083 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/WebClientCustomizer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.client.loadbalancer.reactive; + +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Callback interface that can be used to customize a + * {@link org.springframework.web.reactive.function.client.WebClient.Builder + * WebClient.Builder}. + * + * See original {@link org.springframework.boot.web.reactive.function.client.WebClientCustomizer} + * + * @author Brian Clozel + * @since 2.1.0 + */ +@FunctionalInterface +public interface WebClientCustomizer { + + /** + * Callback to customize a + * {@link org.springframework.web.reactive.function.client.WebClient.Builder + * WebClient.Builder} instance. + * @param webClientBuilder the client builder to customize + */ + void customize(WebClient.Builder webClientBuilder); + +} + diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfigurationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfigurationTests.java index 101931bf..9cb3cf21 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfigurationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfigurationTests.java @@ -16,9 +16,17 @@ package org.springframework.cloud.client.loadbalancer.reactive; +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.Random; + import org.junit.Test; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; @@ -34,12 +42,6 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.WebClient; -import java.io.IOException; -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.Random; - import static org.assertj.core.api.Assertions.assertThat; @@ -90,20 +92,29 @@ public class ReactiveLoadBalancerAutoConfigurationTests { assertThat(getFilters(two.nonLoadBalanced)).isNullOrEmpty(); } + @Test + public void noCustomWebClientBuilders() { + ConfigurableApplicationContext context = init(NoWebClientBuilder.class); + final Map webClientBuilders = context + .getBeansOfType(WebClient.Builder.class); + + assertThat(webClientBuilders).hasSize(1); + + WebClient.Builder builder = context.getBean(WebClient.Builder.class); + + assertThat(builder).isNotNull(); + assertThat(getFilters(builder)).isNullOrEmpty(); + } + protected ConfigurableApplicationContext init(Class config) { return new SpringApplicationBuilder().web(WebApplicationType.NONE) // .properties("spring.aop.proxyTargetClass=true") - .sources(config, ReactiveLoadBalancerAutoConfiguration.class).run(); + .sources(config, WebClientAutoConfiguration.class, + ReactiveLoadBalancerAutoConfiguration.class).run(); } @Configuration - protected static class OneWebClientBuilder { - - @Bean - @LoadBalanced - WebClient.Builder loadBalancedWebClientBuilder() { - return WebClient.builder(); - } + protected static class NoWebClientBuilder { @Bean LoadBalancerClient loadBalancerClient() { @@ -116,7 +127,18 @@ public class ReactiveLoadBalancerAutoConfigurationTests { } @Configuration - protected static class TwoWebClientBuilders { + protected static class OneWebClientBuilder extends NoWebClientBuilder { + + @Bean + @LoadBalanced + WebClient.Builder loadBalancedWebClientBuilder() { + return WebClient.builder(); + } + + } + + @Configuration + protected static class TwoWebClientBuilders extends OneWebClientBuilder { @Primary @Bean @@ -124,17 +146,6 @@ public class ReactiveLoadBalancerAutoConfigurationTests { return WebClient.builder(); } - @LoadBalanced - @Bean - WebClient.Builder loadBalancedWebClientBuilder() { - return WebClient.builder(); - } - - @Bean - LoadBalancerClient loadBalancerClient() { - return new NoopLoadBalancerClient(); - } - @Configuration protected static class Two { @Autowired