文章目录
1、什么是Feign
Feign 是一个声明式 HTTP 调用客户端,Feign 的目标是使编写Java HTTP客户端更加容易。Feign 的声明编程调用思想来自 Retrofit ,Retrofit 在 Android 端十分流行,Feign 借鉴了 Retrofit 的编程理念。
Feign 支持以下几种 HTTP 客户端 Apache Httpclient , Square OkHttp , HttpURLConnection。
2、什么是 Spring Cloud OpenFeign
Spring Cloud 中服务与服务之间的调用都是通过 HTTP 完成的,而 Spring Cloud OpenFeign 就是基于 Fegin 的封装,与Spring Boot 结合实现开箱即用,来实现服务之间内部调用的客户端。
3、Feign 实现原理
以Fegin官方提供的示例(https://github.com/OpenFeign/feign),来解析其实现的原理。如下,获取指定 Github 仓库的所有贡献者:
/**
* 定义声明式接口
*/
interface GitHub {
/**
* 声明调用方式 GET 和 调用地址,获取指定仓库贡献者列表
*/
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repository);
class Contributor {
String login;
int contributions;
}
}
public static void main(String[] args) {
//Feign客户端初始化
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.logLevel(Logger.Level.FULL)
.target(GitHub.class, "https://api.github.com");
//获取并打印feign的贡献者列表。
List<GitHub.Contributor> contributors = github.contributors("OpenFeign", "feign");
contributors.forEach(contributor -> System.out.println(contributor.login + " (" + contributor.contributions + ")"));
}
从示例中可以看到,Feign客户端初始化包括三个部分,Feign 默认配置初始化、设置自定义配置、生成代理对象。
3.1、Feign.builder() 初始化构造器
根据默认值初始化 Feign 构造器
public static Builder builder() {
return new Builder();
}
public static class Builder {
//请求拦截器,拦截每一个请求,添加或修改请求属性
private final List<RequestInterceptor> requestInterceptors =
new ArrayList<RequestInterceptor>();
//日志级别,默认关闭
private Logger.Level logLevel = Logger.Level.NONE;
//定义接口上有效的注释和值。
private Contract contract = new Contract.Default();
//客户端实例化。空参数表示使用默认值。
private Client client = new Client.Default(null, null);
//默认重试规则
private Retryer retryer = new Retryer.Default();
private Logger logger = new NoOpLogger();
//编码器
private Encoder encoder = new Encoder.Default();
//解码器
private Decoder decoder = new Decoder.Default();
//生成查询参数,eg: "/uri?name={name}&number={number}"
private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
//错误解码处理器
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
//控制请求的参数,比如:超时时间
private Options options = new Options();
//反射控制工厂
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
private boolean decode404;
private boolean closeAfterDecode = true;
private ExceptionPropagationPolicy propagationPolicy = NONE;
。。。。。。省略。。。。。。。。
}
3.2、Feign.target() 生成代理对象
public <T> T target(Class<T> apiType, String url) {
return target(new HardCodedTarget<T>(apiType, url));
}
public <T> T target(Target<T> target) {
//调用JDK动态代理生成接口代理类
return build().newInstance(target);
}
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
// 注入依赖配置项
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
public class ReflectiveFeign extends Feign {
@Override
public <T> T newInstance(Target<T> target) {
//根据 Contract 约定,解析接口方法上的注解,建立方法名与对应处理器的映射关系
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
//收集方法对象与对应方法处理器的映射关系
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
//收集默认方法处理器
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 使用JDK动态代理为接口生成代理对象,实际业务处理交给 InvocationHandler 处理,实质就是调用相应