diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index b6590b19..883fdee2 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -196,6 +196,7 @@ feign: capabilities: - com.example.FooCapability - com.example.BarCapability + queryMapEncoder: com.example.SimpleQueryMapEncoder metrics.enabled: false ---- 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 969520c9..ffc6c5f2 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 @@ -68,6 +68,7 @@ import org.springframework.util.StringUtils; * @author Jonatan Ivanov * @author Sam Kruglov * @author Jasbir Singh + * @author Hyeonmin Park */ public class FeignClientFactoryBean implements FactoryBean, InitializingBean, ApplicationContextAware, BeanFactoryAware { @@ -295,6 +296,10 @@ public class FeignClientFactoryBean if (config.getCapabilities() != null) { config.getCapabilities().stream().map(this::getOrInstantiate).forEach(builder::addCapability); } + + if (config.getQueryMapEncoder() != null) { + builder.queryMapEncoder(getOrInstantiate(config.getQueryMapEncoder())); + } } private void addDefaultQueryParams(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) { diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java index a15786ab..20cb6e05 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java @@ -26,6 +26,7 @@ import feign.Capability; import feign.Contract; import feign.ExceptionPropagationPolicy; import feign.Logger; +import feign.QueryMapEncoder; import feign.RequestInterceptor; import feign.Retryer; import feign.codec.Decoder; @@ -40,6 +41,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @author Ram Anaswara * @author Jonatan Ivanov * @author Olga Maciaszek-Sharma + * @author Hyeonmin Park */ @ConfigurationProperties("feign.client") public class FeignClientProperties { @@ -139,6 +141,8 @@ public class FeignClientProperties { private List> capabilities; + private Class queryMapEncoder; + private MetricsProperties metrics; private Boolean followRedirects; @@ -255,6 +259,14 @@ public class FeignClientProperties { this.capabilities = capabilities; } + public Class getQueryMapEncoder() { + return queryMapEncoder; + } + + public void setQueryMapEncoder(Class queryMapEncoder) { + this.queryMapEncoder = queryMapEncoder; + } + public MetricsProperties getMetrics() { return metrics; } @@ -289,7 +301,8 @@ public class FeignClientProperties { && Objects.equals(exceptionPropagationPolicy, that.exceptionPropagationPolicy) && Objects.equals(defaultRequestHeaders, that.defaultRequestHeaders) && Objects.equals(defaultQueryParameters, that.defaultQueryParameters) - && Objects.equals(capabilities, that.capabilities) && Objects.equals(metrics, that.metrics) + && Objects.equals(capabilities, that.capabilities) + && Objects.equals(queryMapEncoder, that.queryMapEncoder) && Objects.equals(metrics, that.metrics) && Objects.equals(followRedirects, that.followRedirects); } @@ -297,7 +310,7 @@ public class FeignClientProperties { public int hashCode() { return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer, errorDecoder, requestInterceptors, decode404, encoder, decoder, contract, exceptionPropagationPolicy, defaultQueryParameters, - defaultRequestHeaders, capabilities, metrics, followRedirects); + defaultRequestHeaders, capabilities, queryMapEncoder, metrics, followRedirects); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java index ef737c91..691844a7 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java @@ -24,6 +24,7 @@ import feign.Capability; import feign.Contract; import feign.ExceptionPropagationPolicy; import feign.Logger; +import feign.QueryMapEncoder; import feign.RequestInterceptor; import feign.Retryer; import feign.codec.Decoder; @@ -43,6 +44,7 @@ import static org.springframework.cloud.openfeign.test.EqualsAndHashCodeAssert.a /** * @author Jonatan Ivanov + * @author Hyeonmin Park */ class FeignClientConfigurationTests { @@ -64,6 +66,7 @@ class FeignClientConfigurationTests { assertThat(config.getContract()).isNull(); assertThat(config.getExceptionPropagationPolicy()).isNull(); assertThat(config.getCapabilities()).isNull(); + assertThat(config.getQueryMapEncoder()).isNull(); assertThat(config.getMetrics()).isNull(); } @@ -88,6 +91,7 @@ class FeignClientConfigurationTests { config.setExceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP); List> capabilities = Lists.list(Capability.class); config.setCapabilities(capabilities); + config.setQueryMapEncoder(QueryMapEncoder.class); FeignClientProperties.MetricsProperties metrics = new FeignClientProperties.MetricsProperties(); config.setMetrics(metrics); @@ -105,6 +109,7 @@ class FeignClientConfigurationTests { assertThat(config.getContract()).isSameAs(Contract.class); assertThat(config.getExceptionPropagationPolicy()).isSameAs(ExceptionPropagationPolicy.UNWRAP); assertThat(config.getCapabilities()).isSameAs(capabilities); + assertThat(config.getQueryMapEncoder()).isSameAs(QueryMapEncoder.class); assertThat(config.getMetrics()).isSameAs(metrics); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java index f14eed1c..dd32004c 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java @@ -37,6 +37,7 @@ import javax.servlet.http.HttpServletRequest; import feign.Capability; import feign.Feign; import feign.InvocationHandlerFactory; +import feign.QueryMapEncoder; import feign.Request; import feign.RequestInterceptor; import feign.RequestTemplate; @@ -78,6 +79,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen * @author Olga Maciaszek-Sharma * @author Ilia Ilinykh * @author Jonatan Ivanov + * @author Hyeonmin Park */ @SuppressWarnings("FieldMayBeFinal") @SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = RANDOM_PORT) @@ -246,6 +248,19 @@ public class FeignClientUsingPropertiesTests { .hasAtLeastOneElementOfType(MicrometerCapability.class); } + @Test + public void clientShouldContainQueryMapEncoder() { + fooFactoryBean.setApplicationContext(applicationContext); + Feign.Builder feignBuilder = fooFactoryBean.feign(context); + FooClient fooClient = feignBuilder.target(FooClient.class, "http://localhost:" + port); + + String response = fooClient.foo(); + assertThat(response).isEqualTo("OK"); + QueryMapEncoder queryMapEncoder = (QueryMapEncoder) ReflectionTestUtils.getField(feignBuilder, + "queryMapEncoder"); + assertThat(queryMapEncoder).isInstanceOf(NoOpQueryMapEncoder.class); + } + @Test @DisabledForJreRange(min = JRE.JAVA_16) public void shouldSetFollowRedirects() { @@ -418,4 +433,13 @@ public class FeignClientUsingPropertiesTests { } + public static class NoOpQueryMapEncoder implements QueryMapEncoder { + + @Override + public Map encode(Object o) { + return null; + } + + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/EqualsAndHashCodeAssert.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/EqualsAndHashCodeAssert.java index 8b00a750..ae003d23 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/EqualsAndHashCodeAssert.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/EqualsAndHashCodeAssert.java @@ -99,7 +99,7 @@ public class EqualsAndHashCodeAssert { */ public static void assertEqualsAndHashCodeConsistency(Object objectOne, Object objectTwo) { assertThat(objectOne.equals(objectTwo)).isTrue(); - assertThat(objectOne.hashCode()).isEqualTo(objectTwo.hashCode()); + assertThat(objectOne).hasSameHashCodeAs(objectTwo); } } diff --git a/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties b/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties index 9ead89f3..41a18f5d 100644 --- a/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties +++ b/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties @@ -9,6 +9,7 @@ feign.client.config.default.errorDecoder=org.springframework.cloud.openfeign.Fei feign.client.config.default.retryer=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.NoRetryer feign.client.config.default.decode404=true feign.client.config.default.capabilities=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.NoOpCapability +feign.client.config.default.queryMapEncoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.NoOpQueryMapEncoder feign.client.config.foo.requestInterceptors[0]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FooRequestInterceptor feign.client.config.foo.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor feign.client.config.singleValue.defaultRequestHeaders[singleValueHeaders]=header