Commit 94d4bdfc by Oreste Luci Committed by Spencer Gibb

Add support for placeholders in @FeignClient definitions

Includes name and url attributes. fixes gh-414
parent 4721bee8
......@@ -790,6 +790,16 @@ NOTE: The `serviceId` attribute is now deprecated in favor of the `name` attribu
WARNING: Previously, using the `url` attribute, did not require the `name` attribute. Using `name` is now required.
Placeholders are supported in the `name` and `url` attributes.
[source,java,indent=0]
----
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
//..
}
----
Spring Cloud Netflix provides the following beans by default for feign (`BeanType` beanName: `ClassName`):
* `Decoder` feignDecoder: `ResponseEntityDecoder` (which wraps a `SpringDecoder`)
......
......@@ -39,14 +39,16 @@ public @interface FeignClient {
/**
* The serviceId with optional protocol prefix. Synonym for {@link #serviceId()
* serviceId}. Either serviceId or url must be specified but not both.
* serviceId}. Either serviceId or url must be specified but not both. Can be
* specified as property key, eg: ${propertyKey}.
*/
@AliasFor("name")
String value() default "";
/**
* The serviceId with optional protocol prefix. Synonym for {@link #value() value}.
* Either serviceId or url must be specified but not both.
* Either serviceId or url must be specified but not both. Can be
* specified as property key, eg: ${propertyKey}.
*
* @deprecated use {@link #name() name} instead
*/
......
......@@ -18,8 +18,10 @@ package org.springframework.cloud.netflix.feign;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
......@@ -34,6 +36,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
......@@ -186,9 +189,11 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
if (!StringUtils.hasText(name)) {
name = (String) attributes.get("value");
}
name = resolve(name);
if (!StringUtils.hasText(name)) {
return "";
}
String host = null;
try {
host = new URI("http://" + name).getHost();
......@@ -199,8 +204,25 @@ public class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
return name;
}
private String resolve(String value) {
if (StringUtils.hasText(value )
&& this.resourceLoader instanceof ConfigurableApplicationContext) {
return ((ConfigurableApplicationContext)this.resourceLoader)
.getEnvironment().resolvePlaceholders(value);
}
return value;
}
private String getUrl(Map<String, Object> attributes) {
return (String) attributes.get("url");
String url = resolve((String) attributes.get("url"));
if (StringUtils.hasText(url)) {
try {
new URL(url);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(url + " is malformed", e);
}
}
return url;
}
protected ClassPathScanningCandidateComponentProvider getScanner() {
......
......@@ -58,6 +58,28 @@ public class FeignClientValidationTests {
}
@Test
public void validPlaceholder() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
PlaceholderUrlConfiguration.class);
assertNotNull(context.getBean(PlaceholderUrlConfiguration.Client.class));
context.close();
}
@Configuration
@Import(FeignAutoConfiguration.class)
@EnableFeignClients(clients = PlaceholderUrlConfiguration.Client.class)
protected static class PlaceholderUrlConfiguration {
@FeignClient(name="example", url="${feignClient.url:http://example.com}")
interface Client {
@RequestMapping(method = RequestMethod.GET, value = "/")
@Deprecated
String get();
}
}
@Test
public void validLoadBalanced() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
RibbonAutoConfiguration.class,
......
......@@ -61,6 +61,9 @@ public class FeignClientScanningTests {
private TestClient testClient;
@Autowired
private TestClientByKey testClientByKey;
@Autowired
private Client feignClient;
@FeignClient("localapp")
......@@ -69,6 +72,12 @@ public class FeignClientScanningTests {
String getHello();
}
@FeignClient("${feignClient.localappName}")
protected interface TestClientByKey {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
String getHello();
}
@Configuration
@EnableAutoConfiguration
@RestController
......@@ -94,6 +103,13 @@ public class FeignClientScanningTests {
assertEquals("first hello didn't match", "hello world 1", hello);
}
@Test
public void testSimpleTypeByKey() {
String hello = this.testClientByKey.getHello();
assertNotNull("hello was null", hello);
assertEquals("first hello didn't match", "hello world 1", hello);
}
// Load balancer with fixed server list for "local" pointing to localhost
@Configuration
public static class LocalRibbonClientConfiguration {
......
......@@ -37,3 +37,5 @@ zuul:
stores:
url: http://localhost:8081
path: /stores/**
feignClient:
localappName: localapp
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment