(一)feign原理解析

Feign是一种声明式、模板化的HTTP客户端。feign中文意思是模仿、假装的,顾名思义,它其实内部封装了http客户端,使得我们调用http请求时就像调用接口那么简单。来看一个简单的feign例子。[1]

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
/**
pom.xml。本文基于当前最新版feign进行解析
<!--feign gson decoder,解析response-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>11.2</version>
</dependency>

<!--feign,feign核心包-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>11.2</version>
</dependency>
**/

import feign.Feign;
import feign.Param;
import feign.RequestLine;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;

import java.io.Serializable;
import java.util.List;

public class FeignDemo {

public static final String API_URL = "https://api.github.com";

//定义返回结果bean
public static class Contributor implements Serializable {
public final String login;
public final int id;
public final String url;

public Contributor(String login, int contributions, String url) {
this.login = login;
this.id = contributions;
this.url = url;
}

}

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

}


public static void main(String[] args) {

GitHub gitHub = Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");

//访问https://api.github.com/repos/OpenFeign/feign/contributors
//看起来就像调用远程接口,实际上是发了http请求。使得代码简洁方便。
List<Contributor> contributors = gitHub.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println("login name=" + contributor.login + ", id = " + contributor.id + ", page url = " + contributor.url);
}
}
}

 以下是大神画的openfeign的原理图,openfeign核心也是feign,接下来我们根据这张图去看看源码如何实现的。本文源码基于feign 11.2解析。

feign原理

1. 创建接口的代理类

  图中的@FeignClient是openfeign中的注解,后面会讲到。根据上面那个例子,从Feign.Builder()入手分析。Feign.Builder中定义了feign的一些属性。

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
//请求拦截器
private final List<RequestInterceptor> requestInterceptors =
new ArrayList<RequestInterceptor>();
//日志级别
private Logger.Level logLevel = Logger.Level.NONE;
//contract:合同。实际上是请求遵守的协议
private Contract contract = new Contract.Default();
//发送请求的客户端。默认为JDK自带的HttpURLConnection
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();
//请求转换成map的类
private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
//报错处理器
private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
//Request的内部类,包含http连接时长、超时设置等信息
private Options options = new Options();
//控制代理类的方法调用。
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
private boolean decode404;
private boolean closeAfterDecode = true;
private ExceptionPropagationPolicy propagationPolicy = NONE;
private boolean forceDecoding = false;
private List<Capability> capabilities = new ArrayList<>();

 接下来分析.target(GitHub.class, "https://api.github.com");target方法。我们可以看到,在target中,build()为feign的相关属性设置了默认值,newInstance最终掉到了feign.ReflectiveFeign#newInstance方法。该方法创建了目标方法的代理类,并为代理类绑定了调用方法MethodHandler。

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
//Feign#target
public <T> T target(Target<T> target) {
return build().newInstance(target);
}

//Feign#build
public Feign build() {
//省略
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
//通过名称解析,规定了request的解析协议,参数,编码/接码器、同步方法处理工厂等,源码不贴了,自己看,很简单
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
//实例化了ReflectiveFeign对象
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

//ReflectiveFeign#newInstance。
public <T> T newInstance(Target<T> target) {
//依据Contract,解析底层MethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

//设置调用方法的handler
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)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
//通过JDK Proxy创建代理类
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);

for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
//返回代理类
return proxy;
}

2. 依据Contract,解析底层MethodHandler

Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);用来解析底层的MethodHandler,我们来看看apply方法。

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
//feign.ReflectiveFeign.ParseHandlersByName#apply

public Map<String, MethodHandler> apply(Target target) {
//1. 解析注解
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
//2. 遍历元数据,解析底层MethodHandler
// 2.1 构造MethodHandler所需的参数BuildTemplateByResolvingArgs
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate =
new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
//2.2 通过工厂构造MethodHandler
if (md.isIgnored()) {
result.put(md.configKey(), args -> {
throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
});
} else {
result.put(md.configKey(),
factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
}
return result;
}
}

2.1 解析注解

 主要对目标接口的注解、方法注解、参数注解进行解析。

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
//feign.Contract.BaseContract#parseAndValidateMetadata(java.lang.Class<?>)
//targetType为目标类。比如例子中为:com.hk7.feign.FeignDemo$GitHub
public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
//目标类必须返回明确的参数。不能是泛型。因此无法存在TypeParameters参数
//假设contributors方法返回类型是List<Contributor>,则会报错,因为无法匹配被调用方具体哪一个方法
checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s",
targetType.getSimpleName());
//最多一个接口。原因同上。
checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s",
targetType.getSimpleName());
if (targetType.getInterfaces().length == 1) {
checkState(targetType.getInterfaces()[0].getInterfaces().length == 0,
"Only single-level inheritance supported: %s",
targetType.getSimpleName());
}
final Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
//遍历接口的所有方法
for (final Method method : targetType.getMethods()) {
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
//解析元数据。解析方法,并设置其处理类MethodHandler
final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s",
metadata.configKey());
result.put(metadata.configKey(), metadata);
}
return new ArrayList<>(result.values());
}


//feign.Contract.BaseContract#parseAndValidateMetadata(java.lang.Class<?>, java.lang.reflect.Method)
//这里是处理接口方法handler的关键代码
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
//定义需要解析的元数据data,并通过targetType和method赋值给它。后面基于data做解析
final MethodMetadata data = new MethodMetadata();
data.targetType(targetType);
data.method(method);
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
//configKey拼接方法,结果是 GitHub#contributors(String,String)
data.configKey(Feign.configKey(targetType, method));

//解析目标类上的注解,本例没有
if (targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
processAnnotationOnClass(data, targetType);

//遍历解析方法上的注解,本例为解析:@RequestLine("GET /repos/{owner}/{repo}/contributors")
for (final Annotation methodAnnotation : method.getAnnotations()) {
processAnnotationOnMethod(data, methodAnnotation, method);
}
//默认false
if (data.isIgnored()) {
return data;
}
//实例化data的时候,template已经创建了,默认为RequestTemplate
//如果RequestTemplate的method为空,则报错
//RequestTemplate的method属性赋值,在解析方法注解的时候完成.后面会讲到
checkState(data.template().method() != null,
"Method %s not annotated with HTTP method type (ex. GET, POST)%s",
data.configKey(), data.warnings());
//获取方法的参数和原生参数,这里为均为String String
final Class<?>[] parameterTypes = method.getParameterTypes();
final Type[] genericParameterTypes = method.getGenericParameterTypes();

//获取参数中的注解,{repo} {repo}等同于@Param
final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
final int count = parameterAnnotations.length;
//遍历处理参数中的注解, @Param
for (int i = 0; i < count; i++) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
//具体处理
isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}

if (isHttpAnnotation) {
data.ignoreParamater(i);
}
//注解解析结果校验
if (parameterTypes[i] == URI.class) {
data.urlIndex(i);
} else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
if (data.isAlreadyProcessed(i)) {
checkState(data.formParams().isEmpty() || data.bodyIndex() == null,
"Body parameters cannot be used with form parameters.%s", data.warnings());
} else {
checkState(data.formParams().isEmpty(),
"Body parameters cannot be used with form parameters.%s", data.warnings());
checkState(data.bodyIndex() == null,
"Method has too many Body parameters: %s%s", method, data.warnings());
data.bodyIndex(i);
data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}
}
//校验header参数
if (data.headerMapIndex() != null) {
checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()],
genericParameterTypes[data.headerMapIndex()]);
}
//校验query参数
if (data.queryMapIndex() != null) {
if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
}
}

return data;
}

 接下来看看,feign是如何对类注解、方法注解和参数注解进行解析的。从以上源码我们可以看到,在feign.DeclarativeContract类中的processAnnotationOnClass、processAnnotationOnMethod、processAnnotationsOnParameter三个方法中对以上三类注解进行了解析。这三个方法大同小异,都是获取定义好的注解解析器,然后调用process方法进行实际解析。

1
2
3
4
5
6
7
8
9
//这里我摘抄了processAnnotationOnMethod的部分源码,另两个类似,只不过多了一些判断条件
//获取所有的方法注解解析器
List<GuardedAnnotationProcessor> processors = methodAnnotationProcessors.stream()
.filter(processor -> processor.test(annotation))
.collect(Collectors.toList());
//遍历方法注解解析器 解析
if (!processors.isEmpty()) {
processors.forEach(processor -> processor.process(annotation, data));
}

 源码我们可以看到方法解析器存储在methodAnnotationProcessorsmap中,那么它是什么时候被放进去的呢?这里往前看,在创建feign实例的时候会执行private Contract contract = new Contract.Default();,在Contract.Default()的构造方法中,把那三种注解解析器注册进了Contract的对应map中。

1
2
3
4
5
6
7
//DeclarativeContract类中存储解析三种注解处理器的三个容器
public abstract class DeclarativeContract extends BaseContract {
private final List<GuardedAnnotationProcessor> classAnnotationProcessors = new ArrayList<>();
private final List<GuardedAnnotationProcessor> methodAnnotationProcessors = new ArrayList<>();
private final Map<Class<Annotation>, DeclarativeContract.ParameterAnnotationProcessor<Annotation>> parameterAnnotationProcessors =
new HashMap<>();
}

Contract.Default继承自DeclarativeContract,从构造方法中可以看到,它的注册方式都是形如super.registerXXXAnnotation(AnnotationClass.class, param -> {})的方式,其中匿名函数param -> {}就是具体解析逻辑。我们跟踪super.registerClassAnnotation方法最终会看到register方法,方法注解和参数注解类似

1
2
3
4
5
6
protected <E extends Annotation> void registerClassAnnotation(Predicate<E> predicate,
DeclarativeContract.AnnotationProcessor<E> processor) {
//GuardedAnnotationProcessor(predicate, processor)就是注解解析器
//其中processor为我们传入的解析器解析方法
this.classAnnotationProcessors.add(new GuardedAnnotationProcessor(predicate, processor));
}

 接下来我们看看具体的注解解析逻辑,代码位于feign.Contract.Default#Default{}

  1. 注册 类注解处理器到classAnnotationProcessors中
1
2
3
4
5
6
7
8
9
10
11
12
//注册 处理注解@Header的类注解器
super.registerClassAnnotation(Headers.class, (header, data) -> {
//获取headers
final String[] headersOnType = header.value();
checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.",
data.configKey());
//放入requestTemplate的headers中
final Map<String, Collection<String>> headers = toMap(headersOnType);
headers.putAll(data.template().headers());
data.template().headers(null); // to clear
data.template().headers(headers);
});

 总结:把@Header中数组取出,转成map赋值给requestTemplate

  1. 注册 方法注解处理器到methodAnnotationProcessors中
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
//1.注册 处理注解@RequestLine的方法注解器
super.registerMethodAnnotation(RequestLine.class, (ann, data) -> {
final String requestLine = ann.value();
checkState(emptyToNull(requestLine) != null,
"RequestLine annotation was empty on method %s.", data.configKey());
//这里对requestLine的值进行正则校验, ^([A-Z]+)[ ]*(.*)$
//匹配 ‘大写字符 小写字符串’。post /a/b/x 失败, Get /a/b/c 成功
final Matcher requestLineMatcher = REQUEST_LINE_PATTERN.matcher(requestLine);
if (!requestLineMatcher.find()) {
throw new IllegalStateException(String.format(
"RequestLine annotation didn't start with an HTTP verb on method %s",
data.configKey()));
} else {
//这里设置requestTemplate的method和url
data.template().method(HttpMethod.valueOf(requestLineMatcher.group(1)));
data.template().uri(requestLineMatcher.group(2));
}
data.template().decodeSlash(ann.decodeSlash());
data.template()
.collectionFormat(ann.collectionFormat());
});


//2.注册 处理注解@Body的方法注解器
super.registerMethodAnnotation(Body.class, (ann, data) -> {
final String body = ann.value();
checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.",
data.configKey());
//这里设置requestTemplate的body
if (body.indexOf('{') == -1) {
data.template().body(body);
} else {
data.template().bodyTemplate(body);
}
});


//3.注册 处理注解@Headers的方法注解器
super.registerMethodAnnotation(Headers.class, (header, data) -> {
final String[] headersOnMethod = header.value();
checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.",
data.configKey());
//这里设置requestTemplate的headers
data.template().headers(toMap(headersOnMethod));
});

 总结:

  • @RequestLine:解析值,并将method和uri赋值给请求(requestTemplate)
  • @Body:设置请求体的值和编码
  • @Headers:设置请求的header
    1. 注册 参数注解处理器到parameterAnnotationProcessors中
    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
    //注册 处理注解@Param的参数注解器
    super.registerParameterAnnotation(Param.class, (paramAnnotation, data, paramIndex) -> {
    final String annotationName = paramAnnotation.value();
    final Parameter parameter = data.method().getParameters()[paramIndex];
    final String name;
    if (emptyToNull(annotationName) == null && parameter.isNamePresent()) {
    name = parameter.getName();
    } else {
    name = annotationName;
    }
    checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.",
    paramIndex);
    nameParam(data, name, paramIndex);
    final Class<? extends Param.Expander> expander = paramAnnotation.expander();
    if (expander != Param.ToStringExpander.class) {
    data.indexToExpanderClass().put(paramIndex, expander);
    }
    //设置data的formParams值
    if (!data.template().hasRequestVariable(name)) {
    data.formParams().add(name);
    }
    });


    //注册 处理注解@QueryMap的参数注解器
    super.registerParameterAnnotation(QueryMap.class, (queryMap, data, paramIndex) -> {
    checkState(data.queryMapIndex() == null,
    "QueryMap annotation was present on multiple parameters.");
    //设置data的queryMapIndex和queryMapEncoded
    data.queryMapIndex(paramIndex);
    data.queryMapEncoded(queryMap.encoded());
    });


    //注册 处理注解@HeaderMap的参数注解器
    super.registerParameterAnnotation(HeaderMap.class, (queryMap, data, paramIndex) -> {
    checkState(data.headerMapIndex() == null,
    "HeaderMap annotation was present on multiple parameters.");
    //设置data的queryMapIndex值
    data.headerMapIndex(paramIndex);
    });
    总结:不总结了,类似method,都是给request设置响应属性值。

 从源码我们可以看到默认的请求协议包含的6种注解,分别是@Header、@RequestLine、@Body、@Headers、@Param、@QueryMap、@HeaderMap,他们对应着三类解析器。实际工作中,我们也可以定义自己的Contract协议,只需要实现Contract接口即可,比如openfeign就是使用这种方式,集成了spring mvc的相关注解进去。

2.2 为代理类设置MethodHandler

 这个很简单,主要是通过工厂创建MethodHandler。factory.create(target, md, buildTemplate, options, decoder, errorDecoder));,这里的factorynew Feign.Build的时候创建的,它是一个同步方法处理工厂SynchronousMethodHandler。接下来我们看看它的create代码。

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 MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
//其实就是返回了一个SynchronousMethodHandler
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
}

//SynchronousMethodHandler类成员变量如下
//具体不解释了,同上。看名字就能猜出来
final class SynchronousMethodHandler implements MethodHandler {
private final MethodMetadata metadata;
private final Target<?> target;
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final Logger logger;
private final Logger.Level logLevel;
private final RequestTemplate.Factory buildTemplateFromArgs;
private final Options options;
private final ExceptionPropagationPolicy propagationPolicy;

// only one of decoder and asyncResponseHandler will be non-null
private final Decoder decoder;
private final AsyncResponseHandler asyncResponseHandler;

 SynchronousMethodHandler继承自MethodHandler,因此方法的调用是在这个类的invoke中进行的。里面包含了图中request构造、编码、请求拦截、response解码等都在里面。

3. 方法的调用和返回

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
@Override
public Object invoke(Object[] argv) throws Throwable {
//1. 构造request
RequestTemplate template = buildTemplateFromArgs.create(argv);
//2. 设置request相关选项。比如连接时长、读取时长等
Options options = findOptions(argv);
//3. 获取重试器
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 4. 发起http请求并解析response
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
//5. 报错重试
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
//4. 记录重试日志
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}

3.1 构造request请求

buildTemplateFromArgs.create(argv);这一句构造request请求,前面分析元数据中也有requestTemplate,它只是临时的,,通过它构造真正的请求。argv是调用方法时传入的参数。

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
@Override
public RequestTemplate create(Object[] argv) {
//1. 通过metadata.template构造临时的RequestTemplate。
//form()就是一些赋值操作,自己去看
RequestTemplate mutable = RequestTemplate.from(metadata.template());
//2. 设置目标url
mutable.feignTarget(target);
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.target(String.valueOf(argv[urlIndex]));
}
//3. 解析argv参数。
//varBuilder {1->OpenFeign, 2->feign}
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
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);
}
}
}
//4. varBuilder参数填充至url中的占位符,并返回真正的RequestTemplate
//实际上是通过Template#resolveExpression解析表达式实现的。不贴源码了,自己去看吧
RequestTemplate template = resolve(argv, mutable, varBuilder);

// 5. queryMap不为空则填充请求的query
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);
}
// 6. headerMap不为空则填充请求的header
if (metadata.headerMapIndex() != null) {
template =
addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}
// 7. 返回构造好的requestTemplate
return template;
}

3.2 发起请求并解析响应

 设置参数和获取重试器都很简单,分析省略。直接看executeAndDecode方法。

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
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//1. 从requestTemplate中获取请求。并执行拦截器的方法
//代码省略,默认的拦截器只是给请求加了Authorization头
Request request = targetRequest(template);
//2. 记日志
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}

Response response;
long start = System.nanoTime();
try {
//3. 客户端发送http请求
//client的初始化再Feign里面,前面有说过。具体的构造就是发送请求并获取response,代码不贴了。使用jdk自带的DelegateHttpsURLConnection连接
/**
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
**/
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

//4. 解码器对response进行解码。默认为空。
if (decoder != null)
return decoder.decode(response, metadata.returnType());
//5. 解析response并解码
//从名字可以看出来,响应处理器是异步的。它会把结果返回到CompletableFuture中
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(),
elapsedTime);

try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");

return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}

 我们来看第五步,解析response并解码源码。

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
void handleResponse(CompletableFuture<Object> resultFuture,
String configKey,
Response response,
Type returnType,
long elapsedTime) {
// copied fairly liberally from SynchronousMethodHandler
boolean shouldClose = true;

try {
if (logLevel != Level.NONE) {
response = logger.logAndRebufferResponse(configKey, logLevel, response,
elapsedTime);
}
//如果是返回的是Response,直接解析
if (Response.class == returnType) {
//body为空直接返回空
if (response.body() == null) {
resultFuture.complete(response);
} else if (response.body().length() == null
|| response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
resultFuture.complete(response);
} else {
// 否则从Response里面读取body,并设置返回
final byte[] bodyData = Util.toByteArray(response.body().asInputStream());
resultFuture.complete(response.toBuilder().body(bodyData).build());
}
//如果返回code是2xx
} else if (response.status() >= 200 && response.status() < 300) {
if (isVoidType(returnType)) {
resultFuture.complete(null);
} else {
// 解码器解析response。具体解析逻辑根据各自传入解码器决定
/**比如gson,主要两步
1. reader读取结果流
Reader reader = response.body().asReader(Util.UTF_8);
2. 转换成json串
this.gson.fromJson(reader, type);
**/
final Object result = decode(response, returnType);
shouldClose = closeAfterDecode;
// 把结果赋值到resultFuture中
resultFuture.complete(result);
}
//如果404,则直接解析并返回
} else if (decode404 && response.status() == 404 && !isVoidType(returnType)) {
final Object result = decode(response, returnType);
shouldClose = closeAfterDecode;
resultFuture.complete(result);
} else {
//如果发生异常(5xx),则异常解析器进行异常解析
resultFuture.completeExceptionally(errorDecoder.decode(configKey, response));
}
} catch (final IOException e) {
if (logLevel != Level.NONE) {
logger.logIOException(configKey, logLevel, e, elapsedTime);
}
resultFuture.completeExceptionally(errorReading(response.request(), response, e));
} catch (final Exception e) {
resultFuture.completeExceptionally(e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}

}

  以上就是feign的工作原理了,基本上和流程图一致,图中没涉及到的@EnableClient和最后的负载均衡,后面解析openfeign原理会讲到。画个图总结下:

feign原理