Make feign.Target.url refreshable (#766)

This commit is contained in:
Jasbir
2022-10-26 15:02:06 +05:30
committed by GitHub
parent 3b02c044cb
commit e33062fac4
12 changed files with 627 additions and 13 deletions

View File

@@ -179,6 +179,7 @@ spring:
client:
config:
feignName:
url: http://remote-service.com
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
@@ -794,7 +795,13 @@ spring.cloud.openfeign.autoconfiguration.jackson.enabled=false
See `org.springframework.cloud.openfeign.FeignAutoConfiguration.FeignJacksonConfiguration` for details.
=== Spring `@RefreshScope` Support
If Feign client refresh is enabled, each feign client is created with `feign.Request.Options` as a refresh-scoped bean. This means properties such as `connectTimeout` and `readTimeout` can be refreshed against any Feign client instance through `POST /actuator/refresh`.
If Feign client refresh is enabled, each Feign client is created with:
* `feign.Request.Options` as a refresh-scoped bean. This means properties such as `connectTimeout` and `readTimeout` can be refreshed against any Feign client instance.
* A url wrapped under `org.springframework.cloud.openfeign.RefreshableUrl`. This means the URL of Feign client, if defined
with `spring.cloud.openfeign.client.config.{feignName}.url` property, can be refreshed against any Feign client instance.
You can refresh these properties through `POST /actuator/refresh`.
By default, refresh behavior in Feign clients is disabled. Use the following property to enable refresh behavior:
[source,java]
@@ -851,6 +858,35 @@ Alternatively, you can use `LoadBalancerFeignRequestTransformer.DEFAULT_ORDER` t
spring.cloud.loadbalancer.x-forwarded.enabled=true
----
=== Supported Ways To Provide URL To A Feign Client
You can provide a URL to a Feign client in any of the following ways:
|===
|Case |Example |Details
|The URL is provided in the `@FeignClient` annotation.
|`@FeignClient(name="testClient", url="http://localhost:8081")`
|The URL is resolved from the `url` attribute of the annotation, without load-balancing.
|The URL is provided in the `@FeignClient` annotation and in the
configuration properties.
|`@FeignClient(name="testClient", url="http://localhost:8081")` and the property defined in `application.yml` as
`spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081`
|The URL is resolved from the `url` attribute of the annotation, without load-balancing.
The URL provided in the configuration properties remains unused.
|The URL is not provided in the `@FeignClient` annotation but is provided in configuration properties.
| `@FeignClient(name="testClient")` and the property defined in `application.yml` as
`spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081`
|The URL is resolved from configuration properties, without load-balancing. If
`spring.cloud.openfeign.client.refresh-enabled=true`, then the URL defined in configuration properties can be refreshed as described in <<Spring `@RefreshScope` Support,Spring RefreshScope Support>>.
|The URL is neither provided in the `@FeignClient` annotation nor in configuration properties.
|`@FeignClient(name="testClient")`
|The URL is resolved from `name` attribute of annotation, with load balancing.
|===
== Configuration properties
To see the list of all Spring Cloud OpenFeign related configuration properties please check link:appendix.html[the Appendix page].

View File

@@ -411,8 +411,7 @@ public class FeignClientFactoryBean
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
@@ -448,7 +447,7 @@ public class FeignClientFactoryBean
applyBuildCustomizers(context, builder);
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
return targeter.target(this, builder, context, (HardCodedTarget<T>) resolveTarget(context, contextId, url));
}
private String cleanPath() {
@@ -467,6 +466,38 @@ public class FeignClientFactoryBean
return path;
}
private <T> HardCodedTarget<T> resolveTarget(FeignContext context, String contextId, String url) {
if (StringUtils.hasText(url)) {
return new HardCodedTarget(type, name, url);
}
if (refreshableClient) {
RefreshableUrl refreshableUrl = context.getInstance(contextId,
RefreshableUrl.class.getCanonicalName() + "-" + contextId, RefreshableUrl.class);
if (Objects.nonNull(refreshableUrl) && StringUtils.hasText(refreshableUrl.getUrl())) {
return new RefreshableHardCodedTarget<>(type, name, refreshableUrl);
}
}
FeignClientProperties.FeignClientConfiguration config = findConfigByKey(contextId);
if (Objects.isNull(config) || !StringUtils.hasText(config.getUrl())) {
throw new IllegalStateException(
"Provide Feign client URL either in @FeignClient() or in config properties.");
}
return new HardCodedTarget(type, name, FeignClientsRegistrar.getUrl(config.getUrl()));
}
private boolean isUrlAvailableInConfig(String contextId) {
FeignClientProperties.FeignClientConfiguration config = findConfigByKey(contextId);
return Objects.nonNull(config) && StringUtils.hasText(config.getUrl());
}
private FeignClientProperties.FeignClientConfiguration findConfigByKey(String configKey) {
FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
: applicationContext.getBean(FeignClientProperties.class);
return properties.getConfig().get(configKey);
}
@Override
public Class<?> getObjectType() {
return type;

View File

@@ -42,6 +42,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Jonatan Ivanov
* @author Olga Maciaszek-Sharma
* @author Hyeonmin Park
* @author Jasbir Singh
*/
@ConfigurationProperties("spring.cloud.openfeign.client")
public class FeignClientProperties {
@@ -147,6 +148,12 @@ public class FeignClientProperties {
private Boolean followRedirects;
/**
* Allows setting Feign client host URL. This value will only be taken into
* account if the url is not set in the @FeignClient annotation.
*/
private String url;
public Logger.Level getLoggerLevel() {
return loggerLevel;
}
@@ -283,6 +290,14 @@ public class FeignClientProperties {
this.followRedirects = followRedirects;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -303,14 +318,14 @@ public class FeignClientProperties {
&& Objects.equals(defaultQueryParameters, that.defaultQueryParameters)
&& Objects.equals(capabilities, that.capabilities)
&& Objects.equals(queryMapEncoder, that.queryMapEncoder) && Objects.equals(metrics, that.metrics)
&& Objects.equals(followRedirects, that.followRedirects);
&& Objects.equals(followRedirects, that.followRedirects) && Objects.equals(url, that.url);
}
@Override
public int hashCode() {
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer, errorDecoder, requestInterceptors,
dismiss404, encoder, decoder, contract, exceptionPropagationPolicy, defaultQueryParameters,
defaultRequestHeaders, capabilities, queryMapEncoder, metrics, followRedirects);
defaultRequestHeaders, capabilities, queryMapEncoder, metrics, followRedirects, url);
}
}

View File

@@ -255,7 +255,8 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
registerOptionsBeanDefinition(registry, contextId);
registerRefreshableBeanDefinition(registry, contextId, Request.Options.class, OptionsFactoryBean.class);
registerRefreshableBeanDefinition(registry, contextId, RefreshableUrl.class, RefreshableUrlFactoryBean.class);
}
private void validate(Map<String, Object> attributes) {
@@ -420,16 +421,17 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
}
/**
* This method is meant to create {@link Request.Options} beans definition with
* refreshScope.
* This method registers beans definition with refreshScope.
* @param registry spring bean definition registry
* @param contextId name of feign client
* @param beanType type of bean
* @param factoryBeanType points to a relevant bean factory
*/
private void registerOptionsBeanDefinition(BeanDefinitionRegistry registry, String contextId) {
private void registerRefreshableBeanDefinition(BeanDefinitionRegistry registry, String contextId, Class<?> beanType,
Class<?> factoryBeanType) {
if (isClientRefreshEnabled()) {
String beanName = Request.Options.class.getCanonicalName() + "-" + contextId;
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(OptionsFactoryBean.class);
String beanName = beanType.getCanonicalName() + "-" + contextId;
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(factoryBeanType);
definitionBuilder.setScope("refresh");
definitionBuilder.addPropertyValue("contextId", contextId);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definitionBuilder.getBeanDefinition(),

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
import feign.Target;
/**
* This target provides url wrapped under {@link Target}.
*
* @author Jasbir Singh
* @since 4.0.0
*/
public class RefreshableHardCodedTarget<T> extends Target.HardCodedTarget<T> {
private RefreshableUrl refreshableUrl;
public RefreshableHardCodedTarget(Class type, String name, RefreshableUrl refreshableUrl) {
super(type, name, refreshableUrl.getUrl());
this.refreshableUrl = refreshableUrl;
}
@Override
public String url() {
return refreshableUrl.getUrl();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
/**
* This class wraps url inside an object so that relevant proxy instance can be created
* using {@link RefreshableUrlFactoryBean}.
*
* @author Jasbir Singh
* @since 4.0.0
*/
public class RefreshableUrl {
private final String url;
public RefreshableUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
import java.util.Objects;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;
/**
* This factory bean creates {@link RefreshableUrl} instance as per the applicable
* configurations.
*
* @author Jasbir Singh
* @since 4.0.0
*/
public class RefreshableUrlFactoryBean implements FactoryBean<RefreshableUrl>, ApplicationContextAware {
private ApplicationContext applicationContext;
private String contextId;
private RefreshableUrl refreshableUrl;
@Override
public Class<?> getObjectType() {
return RefreshableUrl.class;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public RefreshableUrl getObject() throws Exception {
if (refreshableUrl != null) {
return refreshableUrl;
}
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
if (Objects.isNull(properties.getConfig())) {
return new RefreshableUrl(null);
}
FeignClientProperties.FeignClientConfiguration configuration = properties.getConfig().get(contextId);
if (Objects.isNull(configuration) || !StringUtils.hasText(configuration.getUrl())) {
return new RefreshableUrl(null);
}
refreshableUrl = new RefreshableUrl(FeignClientsRegistrar.getUrl(configuration.getUrl()));
return refreshableUrl;
}
public void setContextId(String contextId) {
this.contextId = contextId;
}
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
import feign.Target;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.web.bind.annotation.GetMapping;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jasbir Singh
*/
@SpringBootTest
@TestPropertySource("classpath:feign-properties.properties")
@DirtiesContext
class NonRefreshableFeignClientUrlTests {
@Autowired
private Application.FeignClientWithFixUrl feignClientWithFixUrl;
@Autowired
private Application.ConfigBasedClient configBasedClient;
@Autowired
private Application.NameBasedUrlClient nameBasedUrlClient;
@Test
void shouldInstantiateFeignClientWhenUrlFromFeignClientUrl() {
UrlTestClient.UrlResponseForTests response = feignClientWithFixUrl.fixPath();
assertThat(response.getUrl()).isEqualTo("http://localhost:8081/fixPath");
assertThat(response.getTargetType()).isEqualTo(Target.HardCodedTarget.class);
}
@Test
void shouldInstantiateFeignClientWhenUrlFromFeignClientUrlGivenPreferenceOverProperties() {
UrlTestClient.UrlResponseForTests response = feignClientWithFixUrl.fixPath();
assertThat(response.getUrl()).isEqualTo("http://localhost:8081/fixPath");
}
@Test
public void shouldInstantiateFeignClientWhenUrlFromProperties() {
UrlTestClient.UrlResponseForTests response = configBasedClient.test();
assertThat(response.getUrl()).isEqualTo("http://localhost:9999/test");
assertThat(response.getTargetType()).isEqualTo(Target.HardCodedTarget.class);
}
@Test
void shouldInstantiateFeignClientWhenUrlFromFeignClientName() {
UrlTestClient.UrlResponseForTests response = nameBasedUrlClient.test();
assertThat(response.getUrl()).isEqualTo("http://nameBasedClient/test");
assertThat(response.getTargetType()).isEqualTo(Target.HardCodedTarget.class);
}
@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties(FeignClientProperties.class)
@EnableFeignClients(clients = { Application.FeignClientWithFixUrl.class, Application.ConfigBasedClient.class,
Application.NameBasedUrlClient.class })
protected static class Application {
@Bean
UrlTestClient client() {
return new UrlTestClient();
}
@FeignClient(name = "feignClientWithFixUrl", url = "http://localhost:8081")
protected interface FeignClientWithFixUrl {
@GetMapping("/fixPath")
UrlTestClient.UrlResponseForTests fixPath();
}
@FeignClient(name = "configBasedClient")
protected interface ConfigBasedClient {
@GetMapping("/test")
UrlTestClient.UrlResponseForTests test();
}
@FeignClient(name = "nameBasedClient")
protected interface NameBasedUrlClient {
@GetMapping("/test")
UrlTestClient.UrlResponseForTests test();
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
import feign.Target;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.web.bind.annotation.GetMapping;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jasbir Singh
*/
@SpringBootTest
@TestPropertySource("classpath:feign-refreshable-properties.properties")
@DirtiesContext
class RefreshableFeignClientUrlTests {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private RefreshScope refreshScope;
@Autowired
private RefreshableFeignClientUrlTests.Application.RefreshableClientWithFixUrl refreshableClientWithFixUrl;
@Autowired
private RefreshableFeignClientUrlTests.Application.RefreshableUrlClient refreshableUrlClient;
@Autowired
private Application.RefreshableUrlClientForContextRefreshCase refreshableUrlClientForContextRefreshCase;
@Autowired
private Application.NameBasedUrlClient nameBasedUrlClient;
@Autowired
private FeignClientProperties clientProperties;
@Test
void shouldInstantiateFeignClientWhenUrlFromFeignClientUrl() {
UrlTestClient.UrlResponseForTests response = refreshableClientWithFixUrl.fixPath();
assertThat(response.getUrl()).isEqualTo("http://localhost:8081/fixPath");
assertThat(response.getTargetType()).isEqualTo(Target.HardCodedTarget.class);
}
@Test
void shouldInstantiateFeignClientWhenUrlFromFeignClientUrlGivenPreferenceOverProperties() {
UrlTestClient.UrlResponseForTests response = refreshableClientWithFixUrl.fixPath();
assertThat(response.getUrl()).isEqualTo("http://localhost:8081/fixPath");
}
@Test
public void shouldInstantiateFeignClientWhenUrlFromProperties() {
UrlTestClient.UrlResponseForTests response = refreshableUrlClient.refreshable();
assertThat(response.getUrl()).isEqualTo("http://localhost:8082/refreshable");
assertThat(response.getTargetType()).isEqualTo(RefreshableHardCodedTarget.class);
}
@Test
void shouldInstantiateFeignClientWhenUrlFromPropertiesAndThenUpdateUrlWhenContextRefresh() {
UrlTestClient.UrlResponseForTests response = refreshableUrlClientForContextRefreshCase.refreshable();
assertThat(response.getUrl()).isEqualTo("http://localhost:8080/refreshable");
clientProperties.getConfig().get("refreshableClient").setUrl("http://localhost:8888/");
refreshScope.refreshAll();
response = refreshableUrlClient.refreshable();
assertThat(response.getUrl()).isEqualTo("http://localhost:8888/refreshable");
}
@Test
void shouldInstantiateFeignClientWhenUrlFromFeignClientName() {
UrlTestClient.UrlResponseForTests response = nameBasedUrlClient.nonRefreshable();
assertThat(response.getUrl()).isEqualTo("http://nameBasedClient/nonRefreshable");
assertThat(response.getTargetType()).isEqualTo(Target.HardCodedTarget.class);
}
@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties(FeignClientProperties.class)
@EnableFeignClients(clients = { Application.RefreshableUrlClient.class, Application.NameBasedUrlClient.class,
Application.RefreshableClientWithFixUrl.class,
Application.RefreshableUrlClientForContextRefreshCase.class })
protected static class Application {
@Bean
UrlTestClient client() {
return new UrlTestClient();
}
@FeignClient(name = "refreshableClientWithFixUrl", url = "http://localhost:8081")
protected interface RefreshableClientWithFixUrl {
@GetMapping("/fixPath")
UrlTestClient.UrlResponseForTests fixPath();
}
@FeignClient(name = "refreshableClient")
protected interface RefreshableUrlClient {
@GetMapping("/refreshable")
UrlTestClient.UrlResponseForTests refreshable();
}
@FeignClient(name = "refreshableClientForContextRefreshCase")
protected interface RefreshableUrlClientForContextRefreshCase {
@GetMapping("/refreshable")
UrlTestClient.UrlResponseForTests refreshable();
}
@FeignClient(name = "nameBasedClient")
protected interface NameBasedUrlClient {
@GetMapping("/nonRefreshable")
UrlTestClient.UrlResponseForTests nonRefreshable();
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2013-2022 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
*
* https://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.openfeign;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import feign.Client;
import feign.Request;
import feign.Response;
/**
* @author Jasbir Singh
*/
public class UrlTestClient implements Client {
protected static ObjectMapper mapper;
static {
mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
return Response.builder().status(200).request(request).headers(headers()).body(prepareResponse(request))
.build();
}
private Map<String, Collection<String>> headers() {
Map<String, Collection<String>> headers = new LinkedHashMap<>();
headers.put("Content-Type", Collections.singletonList("application/json"));
return headers;
}
private byte[] prepareResponse(Request request) {
try {
UrlResponseForTests response = new UrlResponseForTests(request.url(),
request.requestTemplate().feignTarget().getClass());
return mapper.writeValueAsString(response).getBytes();
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
static class UrlResponseForTests {
private String url;
private Class<?> targetType;
UrlResponseForTests() {
}
UrlResponseForTests(String url, Class<?> targetType) {
this.url = url;
this.targetType = targetType;
}
void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public Class<?> getTargetType() {
return targetType;
}
public void setTargetType(Class<?> targetType) {
this.targetType = targetType;
}
@Override
public String toString() {
return "UrlResponseForTests{" + "url='" + url + '\'' + ", targetType=" + targetType + '}';
}
}
}

View File

@@ -25,3 +25,5 @@ spring.cloud.openfeign.client.config.unwrap.exceptionPropagationPolicy=unwrap
spring.cloud.openfeign.client.config.readTimeout.readTimeout=1000
spring.cloud.openfeign.client.config.connectTimeout.connectTimeout=1000
spring.cloud.openfeign.client.config.default.followRedirects=false
spring.cloud.openfeign.client.config.feignClientWithFixUrl.url=http://localhost:8888
spring.cloud.openfeign.client.config.configBasedClient.url=http://localhost:9999

View File

@@ -8,3 +8,6 @@ spring.cloud.openfeign.client.config.default.readTimeout=5000
spring.cloud.openfeign.client.config.default.loggerLevel=full
spring.cloud.openfeign.client.config.connectTimeout.connectTimeout=2000
spring.cloud.openfeign.client.config.readTimeout.readTimeout=2000
spring.cloud.openfeign.client.config.refreshableClient.url=http://localhost:8082
spring.cloud.openfeign.client.config.refreshableClientWithFixUrl.url=http://localhost:8888
spring.cloud.openfeign.client.config.refreshableClientForContextRefreshCase.url=http://localhost:8080