Feign是一种声明式、模板化的HTTP客户端。feign中文意思是模仿、假装的,顾名思义,它其实内部封装了http客户端,使得我们调用http请求时就像调用接口那么简单。来看一个简单的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 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
|
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";
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");
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解析。
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;
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();
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; 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
| public <T> T target(Target<T> target) { return build().newInstance(target); }
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); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
public <T> T newInstance(Target<T> target) { 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))); } } InvocationHandler handler = factory.create(target, methodToHandler); 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
|
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type()); Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>(); 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); } 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
|
public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) { 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; } 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()); }
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) { final MethodMetadata data = new MethodMetadata(); data.targetType(targetType); data.method(method); data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); data.configKey(Feign.configKey(targetType, method));
if (targetType.getInterfaces().length == 1) { processAnnotationOnClass(data, targetType.getInterfaces()[0]); } processAnnotationOnClass(data, targetType);
for (final Annotation methodAnnotation : method.getAnnotations()) { processAnnotationOnMethod(data, methodAnnotation, method); } if (data.isIgnored()) { return data; } checkState(data.template().method() != null, "Method %s not annotated with HTTP method type (ex. GET, POST)%s", data.configKey(), data.warnings()); final Class<?>[] parameterTypes = method.getParameterTypes(); final Type[] genericParameterTypes = method.getGenericParameterTypes();
final Annotation[][] parameterAnnotations = method.getParameterAnnotations(); final 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 (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])); } } } if (data.headerMapIndex() != null) { checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]); } 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
|
List<GuardedAnnotationProcessor> processors = methodAnnotationProcessors.stream() .filter(processor -> processor.test(annotation)) .collect(Collectors.toList());
if (!processors.isEmpty()) { processors.forEach(processor -> processor.process(annotation, data)); }
|
源码我们可以看到方法解析器存储在methodAnnotationProcessors
map中,那么它是什么时候被放进去的呢?这里往前看,在创建feign实例的时候会执行private Contract contract = new Contract.Default();
,在Contract.Default()
的构造方法中,把那三种注解解析器注册进了Contract
的对应map中。
1 2 3 4 5 6 7
| 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) { this.classAnnotationProcessors.add(new GuardedAnnotationProcessor(predicate, processor)); }
|
接下来我们看看具体的注解解析逻辑,代码位于feign.Contract.Default#Default{}
中
- 注册 类注解处理器到classAnnotationProcessors中
1 2 3 4 5 6 7 8 9 10 11 12
| super.registerClassAnnotation(Headers.class, (header, data) -> { final String[] headersOnType = header.value(); checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.", data.configKey()); final Map<String, Collection<String>> headers = toMap(headersOnType); headers.putAll(data.template().headers()); data.template().headers(null); data.template().headers(headers); });
|
总结:把@Header中数组取出,转成map赋值给requestTemplate
- 注册 方法注解处理器到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
| super.registerMethodAnnotation(RequestLine.class, (ann, data) -> { final String requestLine = ann.value(); checkState(emptyToNull(requestLine) != null, "RequestLine annotation was empty on method %s.", data.configKey()); 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 { data.template().method(HttpMethod.valueOf(requestLineMatcher.group(1))); data.template().uri(requestLineMatcher.group(2)); } data.template().decodeSlash(ann.decodeSlash()); data.template() .collectionFormat(ann.collectionFormat()); });
super.registerMethodAnnotation(Body.class, (ann, data) -> { final String body = ann.value(); checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.", data.configKey()); if (body.indexOf('{') == -1) { data.template().body(body); } else { data.template().bodyTemplate(body); } });
super.registerMethodAnnotation(Headers.class, (header, data) -> { final String[] headersOnMethod = header.value(); checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.", data.configKey()); data.template().headers(toMap(headersOnMethod)); });
|
总结:
- @RequestLine:解析值,并将method和uri赋值给请求(requestTemplate)
- @Body:设置请求体的值和编码
- @Headers:设置请求的header
- 注册 参数注解处理器到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
| 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); } if (!data.template().hasRequestVariable(name)) { data.formParams().add(name); } });
super.registerParameterAnnotation(QueryMap.class, (queryMap, data, paramIndex) -> { checkState(data.queryMapIndex() == null, "QueryMap annotation was present on multiple parameters."); data.queryMapIndex(paramIndex); data.queryMapEncoded(queryMap.encoded()); });
super.registerParameterAnnotation(HeaderMap.class, (queryMap, data, paramIndex) -> { checkState(data.headerMapIndex() == null, "HeaderMap annotation was present on multiple parameters."); 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));
,这里的factory
是new 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) { return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding); }
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;
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 { RequestTemplate template = buildTemplateFromArgs.create(argv); Options options = findOptions(argv); Retryer retryer = this.retryer.clone(); while (true) { try { return executeAndDecode(template, options); } catch (RetryableException e) { try { 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) { 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) { RequestTemplate mutable = RequestTemplate.from(metadata.template()); 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])); } 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) { if (indexToExpander.containsKey(i)) { value = expandElements(indexToExpander.get(i), value); } for (String name : entry.getValue()) { varBuilder.put(name, value); } } } RequestTemplate template = resolve(argv, mutable, varBuilder); if (metadata.queryMapIndex() != null) { Object value = argv[metadata.queryMapIndex()]; Map<String, Object> queryMap = toQueryMap(value); template = addQueryMapQueryParameters(queryMap, template); } if (metadata.headerMapIndex() != null) { template = addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template); } 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 { Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) { logger.logRequest(metadata.configKey(), logLevel, request); }
Response response; long start = System.nanoTime(); try {
response = client.execute(request, options); 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);
if (decoder != null) return decoder.decode(response, metadata.returnType());
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) {
boolean shouldClose = true;
try { if (logLevel != Level.NONE) { response = logger.logAndRebufferResponse(configKey, logLevel, response, elapsedTime); } if (Response.class == returnType) { 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 { final byte[] bodyData = Util.toByteArray(response.body().asInputStream()); resultFuture.complete(response.toBuilder().body(bodyData).build()); } } else if (response.status() >= 200 && response.status() < 300) { if (isVoidType(returnType)) { resultFuture.complete(null); } else {
final Object result = decode(response, returnType); shouldClose = closeAfterDecode; resultFuture.complete(result); } } else if (decode404 && response.status() == 404 && !isVoidType(returnType)) { final Object result = decode(response, returnType); shouldClose = closeAfterDecode; resultFuture.complete(result); } else { 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原理会讲到。画个图总结下: