diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java index 3b73b09a..d3a78321 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java @@ -16,6 +16,8 @@ package org.springframework.cloud.openfeign; +import feign.Feign; + import org.springframework.context.ApplicationContext; /** @@ -26,6 +28,7 @@ import org.springframework.context.ApplicationContext; * * @author Sven Döring * @author Matt King + * @author Sam Kruglov */ public class FeignClientBuilder { @@ -76,6 +79,18 @@ public class FeignClientBuilder { return this; } + /** + * Applies a {@link FeignBuilderCustomizer} to the underlying + * {@link Feign.Builder}. May be called multiple times. + * @param customizer applied in the same order as supplied here after applying + * customizers found in the context. + * @return the {@link Builder} with the customizer added + */ + public Builder customize(final FeignBuilderCustomizer customizer) { + this.feignClientFactoryBean.addCustomizer(customizer); + return this; + } + public Builder contextId(final String contextId) { this.feignClientFactoryBean.setContextId(contextId); return this; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java index 804f3a09..8def71dd 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java @@ -65,6 +65,7 @@ import org.springframework.util.StringUtils; * @author Ilia Ilinykh * @author Marcin Grzejszczak * @author Jonatan Ivanov + * @author Sam Kruglov */ public class FeignClientFactoryBean implements FactoryBean, InitializingBean, ApplicationContextAware, BeanFactoryAware { @@ -104,6 +105,8 @@ public class FeignClientFactoryBean private boolean followRedirects = new Request.Options().isFollowRedirects(); + private final List additionalCustomizers = new ArrayList<>(); + @Override public void afterPropertiesSet() { Assert.hasText(contextId, "Context id must be set"); @@ -137,6 +140,7 @@ public class FeignClientFactoryBean customizerMap.values().stream().sorted(AnnotationAwareOrderComparator.INSTANCE) .forEach(feignBuilderCustomizer -> feignBuilderCustomizer.customize(builder)); } + additionalCustomizers.forEach(customizer -> customizer.customize(builder)); } protected void configureFeign(FeignContext context, Feign.Builder builder) { @@ -470,6 +474,10 @@ public class FeignClientFactoryBean this.inheritParentContext = inheritParentContext; } + public void addCustomizer(FeignBuilderCustomizer customizer) { + additionalCustomizers.add(customizer); + } + public ApplicationContext getApplicationContext() { return applicationContext; } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java index b08038b3..33de748d 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java @@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Matt King + * @author Sam Kruglov */ public class FeignBuilderCustomizerTests { @@ -74,6 +75,24 @@ public class FeignBuilderCustomizerTests { context.close(); } + @Test + public void testBuildCustomizerOrderedWithAdditional() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + FeignBuilderCustomizerTests.SampleConfiguration3.class); + + FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class); + clientFactoryBean.addCustomizer(builder -> builder.logLevel(Logger.Level.BASIC)); + clientFactoryBean.addCustomizer(Feign.Builder::doNotCloseAfterDecode); + FeignContext feignContext = context.getBean(FeignContext.class); + + Feign.Builder builder = clientFactoryBean.feign(feignContext); + assertFeignBuilderField(builder, "logLevel", Logger.Level.BASIC); + assertFeignBuilderField(builder, "decode404", true); + assertFeignBuilderField(builder, "closeAfterDecode", false); + + context.close(); + } + private static FeignClientFactoryBean defaultFeignClientFactoryBean() { FeignClientFactoryBean feignClientFactoryBean = new FeignClientFactoryBean(); feignClientFactoryBean.setContextId("test"); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java index b90af1d6..c2273830 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import feign.Feign; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; @@ -39,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Sven Döring + * @author Sam Kruglov */ public class FeignClientBuilderTests { @@ -56,6 +58,12 @@ public class FeignClientBuilderTests { private static void assertFactoryBeanField(final FeignClientBuilder.Builder builder, final String fieldName, final Object expectedValue) { + final Object value = getFactoryBeanField(builder, fieldName); + assertThat(value).as("Expected value for the field '" + fieldName + "':").isEqualTo(expectedValue); + } + + @SuppressWarnings("unchecked") + private static T getFactoryBeanField(final FeignClientBuilder.Builder builder, final String fieldName) { final Field factoryBeanField = ReflectionUtils.findField(FeignClientBuilder.Builder.class, "feignClientFactoryBean"); ReflectionUtils.makeAccessible(factoryBeanField); @@ -64,8 +72,7 @@ public class FeignClientBuilderTests { final Field field = ReflectionUtils.findField(FeignClientFactoryBean.class, fieldName); ReflectionUtils.makeAccessible(field); - final Object value = ReflectionUtils.getField(field, factoryBean); - assertThat(value).as("Expected value for the field '" + fieldName + "':").isEqualTo(expectedValue); + return (T) ReflectionUtils.getField(field, factoryBean); } @Before @@ -136,7 +143,7 @@ public class FeignClientBuilderTests { // when: final FeignClientBuilder.Builder builder = this.feignClientBuilder .forType(TestFeignClient.class, new FeignClientFactoryBean(), "TestClient").decode404(true) - .path("Path/").url("Url/").contextId("TestContext"); + .path("Path/").url("Url/").contextId("TestContext").customize(Feign.Builder::doNotCloseAfterDecode); // then: assertFactoryBeanField(builder, "applicationContext", this.applicationContext); @@ -148,6 +155,8 @@ public class FeignClientBuilderTests { assertFactoryBeanField(builder, "url", "http://Url/"); assertFactoryBeanField(builder, "path", "/Path"); assertFactoryBeanField(builder, "decode404", true); + List additionalCustomizers = getFactoryBeanField(builder, "additionalCustomizers"); + assertThat(additionalCustomizers).hasSize(1); } @Test