Feign github 地址
Feign 能做什么 ?
? ? ? ? 借用 README.md 开头的第一句话?Feign makes writing java http clients easier。直接说明了 Feign 的作用是编写 java 的?http clients。
从一个简单的单元测试开始
? ? ? ? 添加?io.github.openfeign:feign-core:10.12?和?com.squareup.okhttp3:mockwebserver:3.14.9?坐标依赖。
import feign.Feign;
import feign.Param;
import feign.RequestLine;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class FeignTest {
public static String baseUrl;
private static final String DEFAULT_BODY = "hello, world!";
@BeforeAll
public static void init() {
// 初始化 mock web server,用于 mock http请求
MockWebServer server = new MockWebServer();
MockResponse mockResponse = new MockResponse().setBody(DEFAULT_BODY);
server.enqueue(mockResponse);
baseUrl = server.url("/default").toString();
}
// 测试客户端的请求模板
interface TestClient {
@RequestLine("GET /test/{owner}")
String test(@Param("owner") String owner);
}
@Test
void testSimpleFeign() {
// 使用 Feign 创建出 TestClient 的实例
TestClient testClient = Feign.builder().target(TestClient.class, baseUrl);
// 发起 http 调用
String result = testClient.test("o");
Assertions.assertEquals(DEFAULT_BODY, result);
}
}
? ? ? ? 通过这个小程序可以看到 Feign 真的可以这个样子发起 http 请求调用,下面看看 Feign 是如何工作的。
? ? ? ? 初步猜测下要想让?TestClient#test() 方法工作起来大概需要以下几步:
- 有?TestClient 接口的实例。
- 解析调用方法中的注解获取参数去构建 http 协议。
- 真正的发起 http 调用。? ?
????????Feign.builder() 会得到 feign.Feign.Builder 这个类的实例。采用建造者模式构建 Feign 需要的相关配置。
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();
// http请求客户端
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();
// client的请求配置
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<>();
......
}
? ? ? ? 特别关注下?Contract ( 解析方法中的相关注解),Client (真正发起 http 调用的客户端),InvocationHandlerFactory (接口代理方法的分发调用)。
/**
* Controls reflective method dispatch.
*/
public interface InvocationHandlerFactory {
InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
/**
* Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
* single method.
*/
interface MethodHandler {
Object invoke(Object[] argv) throws Throwable;
}
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
}
? ? ? ? 在?InvocationHandlerFactory 源码中有个?MethodHandler 接口,上面的注释说明了类似 jdk 动态代理的?java.lang.reflect.InvocationHandler#invoke() 功能,真正执行方法的时候是委托给了?MethodHandler#invoke()。
????????这里 Feign 的大体样子就能想的出来了,那么这些东西是如何串联在一起的?TestClient 实例对象是什么时候创建的?
Feign 是如何生成接口的实现类??
????????Feign.builder().target() 方法返回了?TestClient 实例对象,所以进去看看做了什么。
public static class Builder {
......
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()
// 此处为 ReflectiveFeign实现的newInstance
.newInstance(target);
}
public Feign build() {
// 构建 Feign 的相关组建
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
// 初始化 SynchronousMethodHandler.Factory
// SynchronousMethodHandler 是 MethodHandler 的实现之一
// 执行具体的方法调用
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
// 通过 ParseHandlersByName 得到
// key 为 类名#方法名(参数类型),value 为 MethodHandler 的 map
// 这里 contract 作为参数传到了 ParseHandlersByName 的构造函数
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
// 最终得到 Feign 的实现类之一,将会调用重写的 newInstance 方法
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
......
}
? ? ? ? 到这里可以看出?TestClient 实例对象应该会被?ReflectiveFeign#newInstance() 创建出来。
public class ReflectiveFeign extends Feign {
......
/**
* creates an api binding to the {@code target}. As this invokes reflection, care should be taken
* to cache the result.
*/
@SuppressWarnings("unchecked")
@Override
public <T> T newInstance(Target<T> target) {
// 通过ParseHandlersByName#apply
// 获取格式为 类名#方法名(参数类型) 的方法签名对应的 MethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
// 将 nameToHandler 转换为 methodToHandler
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 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;
}
......
}
? ? ? ? 到这里可以看到是通过 jdk 的动态代理获取了?TestClient 实例对象。
Feign 是如何解析接口方法上的注解 ?
????????ParseHandlersByName#apply 方法进去看看。
static final class ParseHandlersByName {
......
public Map<String, MethodHandler> apply(Target target) {
// 通过 contract 获取 MethodMetadata 列表
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
......
}
......
}
? ? ? ? 这个?MethodMetadata 就是封装好的注解解析的参数。那么?@RequestLine 这些注解在那里定义的?在使用 spring cloud openfeign 使用的是 spring mvc 的注解,那他是怎么自定义的?继续看Contract#parseAndValidateMetadata 方法。
abstract class BaseContract implements Contract {
/**
* @param targetType {@link feign.Target#type() type} of the Feign interface.
* @see #parseAndValidateMetadata(Class)
*/
@Override
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());
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;
}
// 创建 MethodMetadata targetType 为当前例子中的 TestClient
final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
if (result.containsKey(metadata.configKey())) {
MethodMetadata existingMetadata = result.get(metadata.configKey());
Type existingReturnType = existingMetadata.returnType();
Type overridingReturnType = metadata.returnType();
Type resolvedType = Types.resolveReturnType(existingReturnType, overridingReturnType);
if (resolvedType.equals(overridingReturnType)) {
result.put(metadata.configKey(), metadata);
}
continue;
}
result.put(metadata.configKey(), metadata);
}
return new ArrayList<>(result.values());
}
/**
* Called indirectly by {@link #parseAndValidateMetadata(Class)}.
* @param targetType 当前例子中的 TestClient
* @param method 当前例子中的 TestClient#getMethods() 中的每个 Method 对象
*/
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 (AlwaysEncodeBodyContract.class.isAssignableFrom(this.getClass())) {
data.alwaysEncodeBody(true);
}
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 if (!data.alwaysEncodeBody()) {
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;
}
主要是处理类上的注解,方法上的注解,方法参数上的注解。processAnnotationOnClass,processAnnotationOnMethod,processAnnotationOnClass,可以看到这三个方法参数都会传入MethodMetadata,应该是进行了各种参数保存。这三个抽象类由 DeclarativeContract 实现并且提供了相应的注解和MethodMetadata的对应关系,这样不管是 @RequestLine 还是别的注解(比如解析spring mvc的注解),只要指定了和MethodMetadata对应的关系就可以解析了。
// 拿方法上的注解为例
public abstract class DeclarativeContract extends BaseContract {
......
private final List<GuardedAnnotationProcessor> methodAnnotationProcessors = new ArrayList<>();
/**
* @param data metadata collected so far relating to the current java method.
* @param annotation annotations present on the current method annotation.
* @param method method currently being processed.
*/
@Override
protected final void processAnnotationOnMethod(MethodMetadata data,
Annotation annotation,
Method method) {
List<GuardedAnnotationProcessor> processors = methodAnnotationProcessors.stream()
.filter(processor -> processor.test(annotation))
.collect(Collectors.toList());
if (!processors.isEmpty()) {
// 对 methodAnnotationProcessors 中定义的注解和与MethodMetadata的对应关系
// 一顿循环把注解中的值解析到MethodMetadata中
processors.forEach(processor -> processor.process(annotation, data));
} else {
data.addWarning(String.format(
"Method %s has an annotation %s that is not used by contract %s",
method.getName(),
annotation.annotationType()
.getSimpleName(),
getClass().getSimpleName()));
}
}
/**
* Called while method annotations are being processed
*
* 把 注解 和 对注解解析的结果与MethodMetadata对应关系添加到methodAnnotationProcessors集合中
*
* @param annotationType to be processed
* @param processor function that defines the annotations modifies {@link MethodMetadata}
*/
protected <E extends Annotation> void registerMethodAnnotation(Class<E> annotationType,
DeclarativeContract.AnnotationProcessor<E> processor) {
registerMethodAnnotation(
annotation -> annotation.annotationType().equals(annotationType),
processor);
}
/**
* Called while method annotations are being processed
*
* @param predicate to check if the annotation should be processed or not
* @param processor function that defines the annotations modifies {@link MethodMetadata}
*/
protected <E extends Annotation> void registerMethodAnnotation(Predicate<E> predicate,
DeclarativeContract.AnnotationProcessor<E> processor) {
this.methodAnnotationProcessors.add(new GuardedAnnotationProcessor(predicate, processor));
}
......
}
? ? ? ? 到这里猜测下只要随便定义注解和注解与MethodMetadata对应关系就可以工作起来了。这里看看在?feign.Feign.Builder 中?Contract 的实现类就知道是哪些注解了?
private Contract contract = new Contract.Default();
? ? ? ? 看看这个 default 实现做了什么。
class Default extends DeclarativeContract {
static final Pattern REQUEST_LINE_PATTERN = Pattern.compile("^([A-Z]+)[ ]*(.*)$");
public Default() {
// 定义各种注解和与MethodMetadata的对应关系
// λ 表达式就是对应关系的处理
// λ 表达式第一个参数为前面的注解,第二个参数为 MethodMetadata 实例
// 定义类上的注解
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); // to clear
data.template().headers(headers);
});
// 定义方法上的注解
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));
});
// 定义参数上的注解
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);
});
}
......
}
? ? ? ? 熟悉的?@RequestLine 。
Feign 是如何真实的发起 http 请求?
? ? ? ? 调用 TestClient#test() 方法 真正的发起了 http 调用,那么入口在哪??TestClient 的实例是通过 jdk 的动态代理拿到的,这样的话入口应该是?java.lang.reflect.InvocationHandler#invoke() 方法。前面说的?InvocationHandlerFactory (接口代理方法的分发调用)可能是入口。
feign.Feign.Builder中的实现应该是关键。
private InvocationHandlerFactory invocationHandlerFactory =
new InvocationHandlerFactory.Default();
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
// 实现了java.lang.reflect.InvocationHandler
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
// newInstance()得到的接口方法和方法的处理逻辑
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// 具体的实现委派给了MethodHandler的实现类
// 这里是 SynchronousMethodHandler
return dispatch.get(method).invoke(args);
}
......
}
? ? ? ? 终于找到了?java.lang.reflect.InvocationHandler#invoke() 方法,但是真正的逻辑被委派到了?SynchronousMethodHandler#invoke()
final class SynchronousMethodHandler implements MethodHandler {
......
@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;
}
}
}
......
}
? 就这样当调用 TestClient#test() 方法的时候真正的发起了 http 请求。
简单的总结
? ? ? ? Feign 在 build()?的时候通过定义各种配置组件然后调用?newInstance() ,解析注解上面的值,最终得到一个持有?Map<Method, MethodHandler> dispatch 的 动态代理对象。通过调用动态代理对象的方法来实现 http 请求。这是个猜想+验证的过程。
,
|