diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 39956bdf..4da3d150 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -723,6 +723,42 @@ don't want to use Eureka, you can simply configure a list of servers in your external configuration (see <>). +[[spring-cloud-feign-inheritance]] +=== Feign Inheritance Support + +Feign supports boilerplate apis via single-inheritance interfaces. +This allows grouping common operations into convenient base interfaces. +Together with Spring MVC you can share the same contract for your +REST endpoint and Feign client. + +.UserService.java +[source,java,indent=0] +---- +public interface UserService { + + @RequestMapping(method = RequestMethod.GET, value ="/users/{id}") + User getUser(@PathVariable("id") long id); +} +---- + +.UserResource.java +[source,java,indent=0] +---- +@RestController +public class UserResource implements UserService { + +} +---- + +.UserClient.java +[source,java,indent=0] +---- +@FeignClient("users") +public interface UserClient extends UserService { + +} +---- + == External Configuration: Archaius https://github.com/Netflix/archaius[Archaius] is the Netflix client side configuration library. It is the library used by all of the Netflix OSS components for configuration. Archaius is an extension of the http://commons.apache.org/proper/commons-configuration[Apache Commons Configuration] project. It allows updates to configuration by either polling a source for changes or for a source to push changes to the client. Archaius uses DynamicProperty classes as handles to properties. diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignHttpClientTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignHttpClientTests.java index 8034aafb..a0350e19 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignHttpClientTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/feign/valid/FeignHttpClientTests.java @@ -49,6 +49,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.util.ReflectionUtils; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @@ -78,22 +79,34 @@ public class FeignHttpClientTests { @Autowired private Client feignClient; - // @FeignClient(value = "http://localhost:9876", loadbalance = false) + @Autowired + private UserClient userClient; + @FeignClient("localapp") - protected static interface TestClient { + protected interface TestClient extends BaseTestClient { } + + protected interface BaseTestClient { @RequestMapping(method = RequestMethod.GET, value = "/hello") - public Hello getHello(); + Hello getHello(); @RequestMapping(method = RequestMethod.PATCH, value = "/hellop") - public ResponseEntity patchHello(); + ResponseEntity patchHello(); + } + + protected interface UserService { + @RequestMapping(method = RequestMethod.GET, value ="/users/{id}") + User getUser(@PathVariable("id") long id); } + @FeignClient("localapp") + protected interface UserClient extends UserService { } + @Configuration @EnableAutoConfiguration @RestController @EnableFeignClients @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class) - protected static class Application { + protected static class Application implements UserService { @RequestMapping(method = RequestMethod.GET, value = "/hello") public Hello getHello() { @@ -105,6 +118,11 @@ public class FeignHttpClientTests { return ResponseEntity.ok().header("X-Hello", "hello world patch").build(); } + @Override + public User getUser(@PathVariable("id") long id) { + return new User("John Smith"); + } + public static void main(String[] args) { new SpringApplicationBuilder(Application.class).properties( "spring.application.name=feignclienttest", @@ -137,6 +155,14 @@ public class FeignHttpClientTests { assertThat(delegate, is(instanceOf(feign.httpclient.ApacheHttpClient.class))); } + @Test + public void testFeignInheritanceSupport() { + assertNotNull("UserClient was null", userClient); + final User user = userClient.getUser(1); + assertNotNull("Returned user was null", user); + assertEquals("Users were different", user, new User("John Smith")); + } + @Data @AllArgsConstructor @NoArgsConstructor @@ -144,6 +170,13 @@ public class FeignHttpClientTests { private String message; } + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class User { + private String name; + } + // Load balancer with fixed server list for "local" pointing to localhost @Configuration static class LocalRibbonClientConfiguration {