# ​​spring​​ cloud feign

introduction

​netflix feign​​是一个类似​​retrofit​​进行http调用框架,​​Feign makes writing Java http clients easier​​ 使得编写​​http client​​代码更加简单

netflix feign

直接给出一段简单的案例

package com.lkl.netflix.feign;

import feign.*;
import feign.codec.ErrorDecoder;
import feign.codec.Decoder;
import feign.gson.GsonDecoder;

import java.io.IOException;
import java.util.List;

/**
* Created by liaokailin on 16/5/9.
*/
public class GitHubExample {

interface GitHub { // 1
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}


public static void main(String... args) {
Decoder decoder = new GsonDecoder(); // 2
GitHub github = Feign.builder()
.decoder(decoder) // 3
.errorDecoder(new GitHubErrorDecoder(decoder)) // 4
.logger(new Logger.ErrorLogger()) // 5
.logLevel(Logger.Level.BASIC) // 5
.target(GitHub.class, "https://api.github.com"); // 6

System.out.println("Let's fetch and print a list of the contributors to this library.");
List<Contributor> contributors = github.contributors("netflix", "feign"); // 7
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}

System.out.println("Now, let's cause an error.");
try {
github.contributors("netflix", "some-unknown-project"); // 8
} catch (GitHubClientError e) {
System.out.println(e.getMessage());
}
}

static class Contributor {
String login;
int contributions;
}

static class GitHubClientError extends RuntimeException { // 4
private String message; // parsed from json

@Override
public String getMessage() {
return message;
}
}

static class GitHubErrorDecoder implements ErrorDecoder { // 4

final Decoder decoder;
final ErrorDecoder defaultDecoder = new ErrorDecoder.Default();

GitHubErrorDecoder(Decoder decoder) {
this.decoder = decoder;
}

@Override
public Exception decode(String methodKey, Response response) {
try {
return (Exception) decoder.decode(response, GitHubClientError.class);
} catch (IOException fallbackToDefault) {
return defaultDecoder.decode(methodKey, response);
}
}
}


}

​GitHub​​,使用注解​​RequestLine​​ 表明​​contributors()​​方法为一个​​get​​请求,请求相对为​​/repos/{owner}/{repo}/contributors​​ 

2.​​Decoder decoder = new GsonDecoder();​​ 创建一个​​GsonDecoder​​解码器,表明通过​​Gson​​解析返回数据 

3.​​decoder()​​方法设置解码器 

4.​​errorDecoder()​​指定发生异常时的解码器,需要实现​​ErrorDecoder​​接口,覆写​​decode​​方法,通过指定的​​Decoder​​解析错误信息,这里还是使用​​GsonDecoder​​ 

5.logger 相关的表示配置日志系你系 

6.​​target()​​ 方法指定访问url以及返回的类型 

7.通过创建的​​github​​对象调用​​contributors​​获取结果 

8.模拟异常情况​​feign​​使用起来很简单,其原理和​​retrofit​​及其类似,通过接口定义访问访问,用jdk的动态代理创建接口的实现类,在类中解析方法上的注解信息用以识别用户配置的http请求信息,然后执行请求; 

feign可以通过​​ReflectiveFeign​​下的​​newInstance()​​方法看到

return (T) Proxy
.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

spring cloud feign

​spring cloud feign​​通过注解的封装使用起来更加简单

package com.lkl.springcloud.feign;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
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;

import java.util.List;

/**
* Created by liaokailin on 16/5/11.
*/
@Configuration
@EnableAutoConfiguration
@EnableFeignClients
@RestController
public class Application {

@Autowired
GitHub gitHub;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@FeignClient(url = "https://api.github.com")
interface GitHub {
@RequestMapping(value = "/repos/{owner}/{repo}/contributors", method = RequestMethod.GET)
ResponseEntity<List<Contributor>> contributors(@PathVariable("owner") String owner, @PathVariable("repo") String repo);
}

@RequestMapping("/")
public void test(){
ResponseEntity< List<Contributor>> responseEntity = gitHub.contributors("netflix", "feign");
List<Contributor> contributors = responseEntity.getBody();
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}


static class Contributor {
String login;
int contributions;
}
}

​EnableFeignClients​​表明需要扫描使用​​FeignClient​​注解的接口,在代码中定义了​​@FeignClient(url = "https://api.github.com")​​ 表明该接口为一个feign接口 

通过​​url​​指定访问路径​​RequestMapping​​表明相对路径​​GitHub​​,直接调用其中申明的方法即可。​​FeignClient​​注解​​Autowired​​注入使用,如果ribbon在工程中启用,则会使用​​load balance​​进行后端请求调用,可以为​​FeignClient​​指定value表明需要访问的serviceId

feign + ribbon + eureka

在​​springcloud(第七篇)springcloud ribbon with eureka​​​中讲解了ribbon+eureka,其中使用​​RestTemplate​​进行调用,也可以通过 

​feign​​调用

package com.lkl.springcloud.feign;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

/**
* Created by liaokailin on 16/5/9.
*/
@SpringBootApplication
@EnableDiscoveryClient
@RestController
@RibbonClient("hello")
@EnableFeignClients
public class Application {

@Autowired
HelloClient client;

@RequestMapping("/")
public String hello() {
return client.hello();
}

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@FeignClient("simple")
interface HelloClient {
@RequestMapping(value = "/", method = GET)
String hello();
}

}

​@FeignClient("simple")​​指定要访问的​​service id​​ 由于​​hello()​​对应的mapping为​​@RequestMapping(value = "/", method = GET)​​ 那么该方法实际调用url为 

​http://simple/​​,因此在执行

@RequestMapping("/")
public String hello() {
return client.hello();
}

​SimpleApplication.java​​中

@RequestMapping("/")
public String hello() {
return "Hello";
}

ok ~ it’s work ! more about is ​​here​​