feign

Feign是一款java的Restful客户端组件,Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit, JAXRS-2.0和WebSocket。feign在github上有近3K个star,是一款相当优秀的开源组件,虽然相比Retrofit的近30K个star,逊色了太多,但是spring cloud集成了feign,使得feign在java生态中比Retrofit使用的更加广泛。

1.feign介绍

feign的基本原理是在接口方法上加注解,定义rest请求,构造出接口的动态代理对象,然后通过调用接口方法就可以发送http请求,并且自动解析http响应为方法返回值,极大的简化了客户端调用rest api的代码。官网的示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

static class Contributor {
String login;
int contributions;
}

public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");

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

feign使用教程请参考官网https://github.com/OpenFeign/feign/

本文主要是对feign源码进行分析,根据源码来理解feign的设计架构和内部实现技术。

2.Feign.build构建接口动态代理

我们先来看看接口的动态代理是如何构建出来的,下图是主要接口和类的类图:

从上文中的示例可以看到,构建的接口动态代理对象是通过Feign.builder()生成Feign.Builder的构造者对象,然后设置相关的参数,再调用target方法构造的。Feign.Builder的参数包括:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//拦截器,组装完RequestTemplate,发请求之前的拦截处理RequestTemplate
private final List<RequestInterceptor> requestInterceptors = new ArrayList<RequestInterceptor>();
//日志级别
private Logger.Level logLevel = Logger.Level.NONE;
//契约模型,默认为Contract.Default,用户创建MethodMetadata,用spring cloud就是扩展这个实现springMVC注解
private Contract contract = new Contract.Default();
//客户端,默认为Client.Default,可以扩展ApacheHttpClient,OKHttpClient,RibbonClient等
private Client client = new Client.Default(null, null);
//重试设置,默认不设置
private Retryer retryer = new Retryer.Default();
//日志,可以接入Slf4j
private Logger logger = new NoOpLogger();
//编码器,用于body的编码
private Encoder encoder = new Encoder.Default();
//解码器,用户response的解码
private Decoder decoder = new Decoder.Default();
//用@QueryMap注解的参数编码器
private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
//请求错误解码器
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
//参数配置,主要是超时时间之类的
private Options options = new Options();
//动态代理工厂
private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();
//是否decode404
private boolean decode404;
private boolean closeAfterDecode = true;

这块是一个典型的构造者模式,target方法内部先调用build方法新建一个ReflectFeign对象,然后调用ReflectFeignnewInstance方法创建动态代理,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 //默认使用HardCodedTarget
public <T> T target(Class<T> apiType, String url) {
return target(new HardCodedTarget<T>(apiType, url));
}

public <T> T target(Target<T> target) {
return build().newInstance(target);
}

public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
//handlersByName将所有参数进行封装,并提供解析接口方法的逻辑
//invocationHandlerFactory是Builder的属性,默认值是InvocationHandlerFactory.Default,用创建java动态代理的InvocationHandler实现
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}

ReflectiveFeign构造函数有三个参数:

  • ParseHandlersByName 将builder所有参数进行封装,并提供解析接口方法的逻辑
  • InvocationHandlerFactory java动态代理的InvocationHandler的工厂类,默认值是InvocationHandlerFactory.Default
  • QueryMapEncoder 接口参数注解@QueryMap时,参数的编码器

ReflectiveFeign.newInstance方法创建接口动态代理对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public <T> T newInstance(Target<T> target) {
//targetToHandlersByName是构造器传入的ParseHandlersByName对象,根据target对象生成MethodHandler映射
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
//遍历接口所有方法,构建Method->MethodHandler的映射
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if(Util.isDefault(method)) {
//接口default方法的Handler,这类方法直接调用
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
//这里factory是构造其中传入的,创建InvocationHandler
InvocationHandler handler = factory.create(target, methodToHandler);
//java的动态代理
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
//将default方法直接绑定到动态代理上
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}

这段代码主要的逻辑是:

  • 1.创建MethodHandler的映射,这里创建的是实现类SynchronousMethodHandler
  • 2.通过InvocationHandlerFatory创建InvocationHandler
  • 3.绑定接口的default方法,通过DefaultMethodHandler绑定

类图中已经画出,SynchronousMethodHandlerDefaultMethodHandler实现了InvocationHandlerFactory.MethodHandler接口,动态代理对象调用方法时,如果是default方法,会直接调用接口方法,因为这里将接口的default方法绑定到动态代理对象上了,其他方法根据方法签名找到SynchronousMethodHandler对象,调用其invoke方法。

3.创建MethodHandler方法处理器

SynchronousMethodHandler是feign组件的核心,接口方法调用转换为http请求和解析http响应都是通过SynchronousMethodHandler来执行的,相关类图如下:

创建MethodHandler实现类SynchronousMethodHandler的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public Map<String, MethodHandler> apply(Target key) {
//通过contract解析接口方法,生成MethodMetadata列表,默认的contract解析Feign自定义的http注解
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
//BuildTemplateByResolvingArgs实现RequestTemplate.Factory,RequestTemplate的工厂
BuildTemplateByResolvingArgs buildTemplate;
//根据方法元数据,使用不同的RequestTemplate的工厂
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
//如果有formParam,并且bodyTemplate不为空,请求体为x-www-form-urlencoded格式
//将会解析form参数,填充到bodyTemplate中
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else if (md.bodyIndex() != null) {
//如果包含请求体,将会用encoder编码请求体对象
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
} else {
//默认的RequestTemplate的工厂,没有请求体,不需要编码器
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
}
//使用工厂SynchronousMethodHandler.Factory创建SynchronousMethodHandler
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}

这段代码的逻辑是:

  • 1.通过Contract解析接口方法,生成MethodMetadata,默认的Contract解析Feign自定义的http注解
  • 2.根据MethodMetadata方法元数据生成特定的RequestTemplate的工厂
  • 3.使用SynchronousMethodHandler.Factory工厂创建SynchronousMethodHandler
    这里有两个工厂不要搞混淆了,SynchronousMethodHandler工厂和RequestTemplate工厂,SynchronousMethodHandler的属性包含RequestTemplate工厂

4.Contract解析接口方法生成MethodMetadata

feign默认的解析器是Contract.Default继承了Contract.BaseContract,解析生成MethodMetadata方法入口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
@Override
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
。。。
Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
for (Method method : targetType.getMethods()) {
。。。
MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
。。。
result.put(metadata.configKey(), metadata);
}
return new ArrayList<MethodMetadata>(result.values());
}

protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
MethodMetadata data = new MethodMetadata();
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
data.configKey(Feign.configKey(targetType, method));

if(targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
//处理Class上的注解
processAnnotationOnClass(data, targetType);

for (Annotation methodAnnotation : method.getAnnotations()) {
//处理方法注解
processAnnotationOnMethod(data, methodAnnotation, method);
}
。。。
Class<?>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
//方法参数注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int count = parameterAnnotations.length;
for (int i = 0; i < count; i++) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}
if (parameterTypes[i] == URI.class) {
//参数类型是URI,后面构造http请求时,使用该URI
data.urlIndex(i);
} else if (!isHttpAnnotation) {
//如果没有被http注解,就是body参数
。。。
data.bodyIndex(i);
data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}

if (data.headerMapIndex() != null) {
//@HeaderMap注解的参数必须是Map,key类型必须是String
checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]);
}

if (data.queryMapIndex() != null) {
if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
//@QueryMap注解的参数如果是Map,key类型必须是String
checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
}
}
return data;
}

protected void processAnnotationOnClass(MethodMetadata data, Class<?> targetType) {
if (targetType.isAnnotationPresent(Headers.class)) {
//被Headers注解
String[] headersOnType = targetType.getAnnotation(Headers.class).value();
。。。
//header解析成map,加到MethodMetadata中
Map<String, Collection<String>> headers = toMap(headersOnType);
headers.putAll(data.template().headers());
data.template().headers(null); // to clear
data.template().headers(headers);
}
}

protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation,
Method method) {
Class<? extends Annotation> annotationType = methodAnnotation.annotationType();
if (annotationType == RequestLine.class) {
//@RequestLine注解
String requestLine = RequestLine.class.cast(methodAnnotation).value();
。。。
if (requestLine.indexOf(' ') == -1) {
。。。
data.template().method(requestLine);
return;
}
//http请求方法
data.template().method(requestLine.substring(0, requestLine.indexOf(' ')));
if (requestLine.indexOf(' ') == requestLine.lastIndexOf(' ')) {
// no HTTP version is ok
data.template().append(requestLine.substring(requestLine.indexOf(' ') + 1));
} else {
// skip HTTP version
data.template().append(
requestLine.substring(requestLine.indexOf(' ') + 1, requestLine.lastIndexOf(' ')));
}
//将'%2F'反转为'/'
data.template().decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash());
//参数集合格式化方式,默认使用key=value0&key=value1
data.template().collectionFormat(RequestLine.class.cast(methodAnnotation).collectionFormat());

} else if (annotationType == Body.class) {
//@Body注解
String body = Body.class.cast(methodAnnotation).value();
。。。
if (body.indexOf('{') == -1) {
//body中不存在{,直接传入body
data.template().body(body);
} else {
//body中存在{,就是bodyTemplate方式
data.template().bodyTemplate(body);
}
} else if (annotationType == Headers.class) {
//@Header注解
String[] headersOnMethod = Headers.class.cast(methodAnnotation).value();
。。。
data.template().headers(toMap(headersOnMethod));
}
}

//处理参数上的注解
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
boolean isHttpAnnotation = false;
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType == Param.class) {
//@Param注解
Param paramAnnotation = (Param) annotation;
String name = paramAnnotation.value();
。。。
//增加到MethodMetadata中
nameParam(data, name, paramIndex);
//@Param注解的expander参数,定义参数的解释器,默认是ToStringExpander,调用参数的toString方法
Class<? extends Param.Expander> expander = paramAnnotation.expander();
if (expander != Param.ToStringExpander.class) {
data.indexToExpanderClass().put(paramIndex, expander);
}
//参数是否已经urlEncoded,如果没有,会使用urlEncoded方式编码
data.indexToEncoded().put(paramIndex, paramAnnotation.encoded());
isHttpAnnotation = true;
String varName = '{' + name + '}';
if (!data.template().url().contains(varName) &&
!searchMapValuesContainsSubstring(data.template().queries(), varName) &&
!searchMapValuesContainsSubstring(data.template().headers(), varName)) {
//如果参数不在path里面,不在query里面,不在header里面,就设置到formParam中
data.formParams().add(name);
}
} else if (annotationType == QueryMap.class) {
//@QueryMap注解,注解参数对象时,将该参数转换为http请求参数格式发送
。。。
data.queryMapIndex(paramIndex);
data.queryMapEncoded(QueryMap.class.cast(annotation).encoded());
isHttpAnnotation = true;
} else if (annotationType == HeaderMap.class) {
//@HeaderMap注解,注解一个Map类型的参数,放入http header中发送
。。。
data.headerMapIndex(paramIndex);
isHttpAnnotation = true;
}
}
return isHttpAnnotation;
}

代码稍微有点多,但是逻辑很清晰,先处理类上的注解,再处理方法上注解,最后处理方法参数注解,把所有注解的情况都处理到就可以了。

生成的MethodMetadata的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public final class MethodMetadata implements Serializable {
//标识方法的key,接口名加方法签名:GitHub#contributors(String,String)
private String configKey;
//方法返回值类型
private transient Type returnType;
//uri参数的位置,方法中可以写个uri参数,发请求时直接使用这个参数
private Integer urlIndex;
//body参数的位置,只能有一个未注解的参数为body,否则报错
private Integer bodyIndex;
//headerMap参数的位置
private Integer headerMapIndex;
//@QueryMap注解参数位置
private Integer queryMapIndex;
//@QueryMap注解里面encode参数,是否已经urlEncode编码过了
private boolean queryMapEncoded;
//body的类型
private transient Type bodyType;
//RequestTemplate 原型
private RequestTemplate template = new RequestTemplate();
//form请求参数
private List<String> formParams = new ArrayList<String>();
//方法参数位置和名称的map
private Map<Integer, Collection<String>> indexToName ;
//@Param中注解的expander方法,可以指定解析参数类
private Map<Integer, Class<? extends Expander>> indexToExpanderClass ;
//参数是否被urlEncode编码过了,@Param中encoded方法
private Map<Integer, Boolean> indexToEncoded ;
//自定义的Expander
private transient Map<Integer, Expander> indexToExpander;

Contract也是feign的一个扩展点,一个优秀组件的架构通常是具有很强的扩展性,feign的架构本身很简单,设计的扩展点也很简单方便,所以受到spring的青睐,将其集成到spring cloud中。spring cloud就是通过Contract的扩展,实现使用springMVC的注解接入feign。feign自己还实现了使用jaxrs注解接入feign。

5.初始化总结

上文已经完成了feign初始化结构为动态代理的整个过程,简单的捋一遍:

  1. 初始化Feign.Builder传入参数,构造ReflectiveFeign
  2. ReflectiveFeign通过内部类ParseHandlersByName的Contract属性,解析接口生成MethodMetadata
  3. ParseHandlersByName根据MethodMetadata生成RequestTemplate工厂
  4. ParseHandlersByName创建SynchronousMethodHandler,传入MethodMetadataRequestTemplate工厂和Feign.Builder相关参数
  5. ReflectiveFeign创建FeignInvocationHandler,传入参数SynchronousMethodHandler,绑定DefaultMethodHandler
  6. ReflectiveFeign根据FeignInvocationHandler创建Proxy

关键的几个类是:

  • ReflectiveFeign 初始化入口
  • FeignInvocationHandler 实现动态代理的InvocHandler
  • SynchronousMethodHandler 方法处理器,方法调用处理器
  • MethodMetadata 方法元数据

6.接口调用

为方便理解,分析完feign源码后,我将feign执行过程分成三层,如下图:

三层分别为:

  • 代理层 动态代理调用层
  • 转换层 方法转http请求,解码http响应
  • 网络层 http请求发送

java动态代理接口方法调用,会调用到InvocaHandler的invoke方法,feign里面实现类是FeignInvocationHandler,invoke代码如下:

1
2
3
4
5
6
private final Map<Method, MethodHandler> dispatch;

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
。。。
return dispatch.get(method).invoke(args);
}

根据方法找到MethodHandler,除接口的default方法外,找到的是SynchronousMethodHandler对象,然后调用SynchronousMethodHandlerd.invoke方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public Object invoke(Object[] argv) throws Throwable {
//buildTemplateFromArgs是RequestTemplate工程对象,根据方法参数创建RequestTemplate
RequestTemplate template = buildTemplateFromArgs.create(argv);
//重试设置
Retryer retryer = this.retryer.clone();
while (true) {
try {
//执行和解码
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
。。。
continue;
}
}
}

Object executeAndDecode(RequestTemplate template) throws Throwable {
//RequestTemplate转换为Request
Request request = targetRequest(template)
。。。
Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options);
response.toBuilder().request(request).build();
} catch (IOException e) {
。。。
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

boolean shouldClose = true;
try {
。。。
if (Response.class == metadata.returnType()) {
//如果接口方法返回的是Response类
if (response.body() == null) {
//body为空,直接返回
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
//body不为空,且length>最大缓存值,返回response,但是不能关闭response
shouldClose = false;
return response;
}
// 读取body字节数组,返回response
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
//响应成功
if (void.class == metadata.returnType()) {
//接口返回void
return null;
} else {
//解码response,直接调用decoder解码
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
//404解析
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
} else {
//其他返回码,使用errorDecoder解析,抛出异常
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
throw errorReading(request, response, e);
} finally {
//是否需要关闭response,根据Feign.Builder 参数设置是否要关闭流
if (shouldClose) {
ensureClosed(response.body());
}
}
}

过程比较简单,生成RquestTemplate -> 转换为Request -> client发请求 -> Decoder解析Response

RquestTemplate构建过程

先看看RequestTemplate的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static final long serialVersionUID = 1L;
//请求参数 ?后面的name=value
private final Map<String, Collection<String>> queries ;
//请求头
private final Map<String, Collection<String>> headers ;
//请求方法 GET/POST等
private String method;
//请求路径
private StringBuilder url = new StringBuilder();
//字符集
private transient Charset charset;
//请求体
private byte[] body;
//@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")注解的模板
private String bodyTemplate;
//是否decode削减,将"%2F"反转为"/"
private boolean decodeSlash = true;
//集合格式化,分隔符
private CollectionFormat collectionFormat = CollectionFormat.EXPLODED;

SynchronousMethodHandler.invoke方法中生成RequestTemplate

1
2
//buildTemplateFromArgs是RequestTemplate.Factory实现类
RequestTemplate template = buildTemplateFromArgs.create(argv);

RequestTemplate.Factory有三个实现类:

  • BuildTemplateByResolvingArgs RequestTemplate工厂
  • BuildEncodedTemplateFromArgs BuildTemplateByResolvingArgs的子类 重载resolve方法,解析form表单请求
  • BuildFormEncodedTemplateFromArgs BuildTemplateByResolvingArgs的子类,重载resolve方法,解析body请求

BuildTemplateByResolvingArgs创建RequestTemplatecreate方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//BuildTemplateByResolvingArgs实现RequestTemplate.Factory的方法
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = new RequestTemplate(metadata.template());
if (metadata.urlIndex() != null) {
//插入接口方法参数中的URI
int urlIndex = metadata.urlIndex();
mutable.insert(0, String.valueOf(argv[urlIndex]));
}
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
//方法参数位置和请求定义的参数名称的map
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
//将方法参数值和定义的请求参数进行映射,varBuilder
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
//解析RequestTemplate
RequestTemplate template = resolve(argv, mutable, varBuilder);
//解析queryMap,这块代码有些奇怪,为什么单独把queryMap放在这里解析,而不是在resolve方法中,或者在RequestTemplate中
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
//解析headerMap定义的参数
if (metadata.headerMapIndex() != null) {
template = addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}

return template;
}

//BuildTemplateByResolvingArgs
protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable,
Map<String, Object> variables) {
// 根据需要进行urlEncode参数
Map<String, Boolean> variableToEncoded = new LinkedHashMap<String, Boolean>();
for (Entry<Integer, Boolean> entry : metadata.indexToEncoded().entrySet()) {
Collection<String> names = metadata.indexToName().get(entry.getKey());
for (String name : names) {
variableToEncoded.put(name, entry.getValue());
}
}
//解析参数
return mutable.resolve(variables, variableToEncoded);
}

//BuildEncodedTemplateFromArgs
protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable,
Map<String, Object> variables) {
Object body = argv[metadata.bodyIndex()];
checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex());
try {
//编码并设置RequestTemplate的body
encoder.encode(body, metadata.bodyType(), mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
}

//BuildFormEncodedTemplateFromArgs
protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable,
Map<String, Object> variables) {
//构造form参数,为HashMap
Map<String, Object> formVariables = new LinkedHashMap<String, Object>();
for (Entry<String, Object> entry : variables.entrySet()) {
if (metadata.formParams().contains(entry.getKey())) {
formVariables.put(entry.getKey(), entry.getValue());
}
}
try {
//编码并设置RequestTemplate的body,
encoder.encode(formVariables, Encoder.MAP_STRING_WILDCARD, mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
//调用父类的resolve
return super.resolve(argv, mutable, variables);
}

RequestTemplate解析参数的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//RequestTemplate解析参数的方法
public RequestTemplate resolve(Map<String, ?> variables) {

StringBuilder uri = new StringBuilder();

/* create a new template form this one, but explicitly */
RequestTemplate resolved = RequestTemplate.from(this);

if (this.uriTemplate == null) {
/* create a new uri template using the default root */
this.uriTemplate = UriTemplate.create("", !this.decodeSlash, this.charset);
}

uri.append(this.uriTemplate.expand(variables));

/*
* for simplicity, combine the queries into the uri and use the resulting uri to seed the
* resolved template.
*/
if (!this.queries.isEmpty()) {
/*
* since we only want to keep resolved query values, reset any queries on the resolved copy
*/
resolved.queries(Collections.emptyMap());
StringBuilder query = new StringBuilder();
Iterator<QueryTemplate> queryTemplates = this.queries.values().iterator();

while (queryTemplates.hasNext()) {
QueryTemplate queryTemplate = queryTemplates.next();
String queryExpanded = queryTemplate.expand(variables);
if (Util.isNotBlank(queryExpanded)) {
query.append(queryTemplate.expand(variables));
if (queryTemplates.hasNext()) {
query.append("&");
}
}
}

String queryString = query.toString();
if (!queryString.isEmpty()) {
Matcher queryMatcher = QUERY_STRING_PATTERN.matcher(uri);
if (queryMatcher.find()) {
/* the uri already has a query, so any additional queries should be appended */
uri.append("&");
} else {
uri.append("?");
}
uri.append(queryString);
}
}

/* add the uri to result */
resolved.uri(uri.toString());

/* headers */
if (!this.headers.isEmpty()) {
/*
* same as the query string, we only want to keep resolved values, so clear the header map on
* the resolved instance
*/
resolved.headers(Collections.emptyMap());
for (HeaderTemplate headerTemplate : this.headers.values()) {
/* resolve the header */
String header = headerTemplate.expand(variables);
if (!header.isEmpty()) {
/* split off the header values and add it to the resolved template */
String headerValues = header.substring(header.indexOf(" ") + 1);
if (!headerValues.isEmpty()) {
resolved.header(headerTemplate.getName(), headerValues);
}
}
}
}

resolved.body(this.body.expand(variables));

/* mark the new template resolved */
resolved.resolved = true;
return resolved;
}

8.RquestTemplate转换Request

先来看看Request的结构,完整的http请求信息的定义:

1
2
3
4
5
private final String method;
private final String url;
private final Map<String, Collection<String>> headers;
private final byte[] body;
private final Charset charset;

SynchronousMethodHandlertargetRequest方法将RequestTemplate转换为Request

1
2
3
4
5
6
7
8
Request targetRequest(RequestTemplate template) {
//先应用所用拦截器,拦截器是在Feign.Builder中传入的,拦截器可以修改RequestTemplate信息
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
//调用Target的apply方法,默认Target是HardCodedTarget
return target.apply(new RequestTemplate(template));
}

这块先应用所有拦截器,然后target的apply方法。拦截器和target都是扩展点,拦截器可以在构造好RequestTemplate后和发请求前修改请求信息,target默认使用HardCodedTarget直接发请求,feign还提供了LoadBalancingTarget,适配Ribbon来发请求,实现客户端的负载均衡。

创建过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 //HardCodedTarget的apply方法
public Request apply(RequestTemplate input) {
if (input.url().indexOf("http") != 0) {
input.insert(0, url());
}
//调用RequestTemplate的request方法
return input.request();
}

//RequestTemplate的request方法
public Request request() {
//安全拷贝所有header
Map<String, Collection<String>> safeCopy = new LinkedHashMap<String, Collection<String>>();
safeCopy.putAll(headers);
//调用Request的create静态方法
return Request.create(
method, url + queryLine(),
Collections.unmodifiableMap(safeCopy),
body, charset
);
}

//Request的create方法
public static Request create(String method, String url, Map<String, Collection<String>> headers,
byte[] body, Charset charset) {
//new 对象
return new Request(method, url, headers, body, charset);
}

从代码上可以看到,RequestTemplate基本上直接转为Request,没有做什么逻辑操作。对比下LoadBalancingTarget

1
2
3
4
5
6
7
8
9
10
11
12
13
public Request apply(RequestTemplate input) {
//选取一个Server,lb是Ribbon的AbstractLoadBalancer类
Server currentServer = lb.chooseServer(null);
//生成url
String url = format("%s://%s%s", scheme, currentServer.getHostPort(), path);
input.insert(0, url);
try {
//生成Request
return input.request();
} finally {
lb.getLoadBalancerStats().incrementNumRequests(currentServer);
}
}

可以看到,非常简单的几行代码,只要修改请求的url就能实现客户端负载均衡。

9.http请求发送

SynchronousMethodHandler中构造好Request后,直接调用clientexecute方法发送请求:

1
response = client.execute(request, options);

client是一个Client接口,默认实现类是Client.Default,使用java api中的HttpURLConnection发送http请求。feign还实现了:

  • ApacheHttpClient
  • OkHttpClient
  • RibbonClient
    使用RibbonClient跟使用LoadBalancingTarget作用都是实现客户端负载均衡,RibbonClient实现稍微复杂些。

10.接口调用过程总结

我们再将接口调用过程捋一遍:

  1. 接口的动态代理Proxy调用接口方法会执行的FeignInvocationHandler
  2. FeignInvocationHandler通过方法签名在属性Map<Method, MethodHandler> dispatch中找到SynchronousMethodHandler,调用invoke方法
  3. SynchronousMethodHandlerinvoke方法根据传入的方法参数,通过自身属性工厂对象RequestTemplate.Factory创建RequestTemplate,工厂里面会用根据需要进行Encode
  4. SynchronousMethodHandler遍历自身属性RequestInterceptor列表,对RequestTemplate进行改造
  5. SynchronousMethodHandler调用自身Target属性的apply方法,将RequestTemplate转换为Request对象
  6. SynchronousMethodHandler调用自身Clientexecute方法,传入Request对象
  7. Client将Request转换为http请求,发送后将http响应转换为Response对象
  8. SynchronousMethodHandler调用Decoder的方法对Response对象解码后返回
  9. 返回的对象最后返回到Proxy

时序图如下:

11.feign扩展点总结

前文分析源代码时,已经提到了feign的扩展点,最后我们再将feign的主要扩展点进行总结一下:

  • Contract 契约
    Contract的作用是解析接口方法,生成Rest定义。feign默认使用自己的定义的注解,还提供了

    • JAXRSContract javax.ws.rs注解接口实现
    • SpringContract是spring cloud提供SpringMVC注解实现方式。
  • InvocationHandler 动态代理handler
    通过InvocationHandlerFactory注入到Feign.Builder中,feign提供了Hystrix的扩展,实现Hystrix接入

  • Encoder 请求body编码器
    feign已经提供扩展包含:

    • 默认编码器,只能处理String和byte[]
    • json编码器GsonEncoderJacksonEncoder
    • XML编码器JAXBEncoder
  • Decoder http响应解码器
    最基本的有:

    • json解码器 GsonDecoderJacksonDecoder
    • XML解码器 JAXBDecoder
    • Stream流解码器 StreamDecoder
  • Target 请求转换器
    feign提供的实现有:

    • HardCodedTarget 默认Target,不做任何处理。
    • LoadBalancingTarget 使用Ribbon进行客户端路由
  • Client 发送http请求的客户端
    feign提供的Client实现有:

    • Client.Default 默认实现,使用java api的HttpClientConnection发送http请求
    • ApacheHttpClient 使用apache的Http客户端发送请求
    • OkHttpClient 使用OKHttp客户端发送请求
    • RibbonClient 使用Ribbon进行客户端路由
  • RequestInterceptor 请求拦截器
    调用客户端发请求前,修改RequestTemplate,比如为所有请求添加Header就可以用拦截器实现。

  • Retryer 重试策略
    默认的策略是Retryer.Default,包含3个参数:间隔、最大间隔和重试次数,第一次失败重试前会sleep输入的间隔时间的,后面每次重试sleep时间是前一次的1.5倍,超过最大时间或者最大重试次数就失败


本文转载:拍拍贷基础框架团队博客