在之前的两篇博文《OkHttp的高级封装Feign学习(一): Feign注解的使用》和《OpenFeign学习(二):高级用法自定义配置组件HttpClient / SLF4J / RequestInterceptor等》中,我简单介绍了OpenFeign的使用方法。在本篇博文中,我将通过源码来学习记录OpenFeign的工作原理。
在阅读源码前,我们先通过流程图来了解OpenFeign的具体工作流程:
如图所示,我把OpenFeign的工作流程分为了10个阶段。接下来通过阅读源码来了解每个阶段是如何实现的。
在之前的博文中我们已经学习了如何使用OpenFeign来进行服务的调用,在接口方法中使用注释,最后通过Feign的构造器来创建接口的代理。
HelloService service = Feign.builder()
.logger(new Slf4jLogger())
.logLevel(Logger.Level.FULL)
.client(new OkHttpClient())
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.requestInterceptor(new HeadersInterceptor())
.errorDecoder(new CustomErrorDecoder())
.retryer(new MyRetryer(5))
.exceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP)
.target(HelloService.class, "http://localhost:8080/");
以上示例代码说明了通过Feign的构造器,我们可以自定义配置其中的组件,如client,encoder,decoder等。通过该构造器的源码来了解下未配置组件时的默认值。
public Builder() {
this.logLevel = Level.NONE;
this.contract = new Default();
this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
this.retryer = new feign.Retryer.Default();
this.logger = new NoOpLogger();
this.encoder = new feign.codec.Encoder.Default();
this.decoder = new feign.codec.Decoder.Default();
this.queryMapEncoder = new FieldQueryMapEncoder();
this.errorDecoder = new feign.codec.ErrorDecoder.Default();
this.options = new Options();
this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
this.closeAfterDecode = true;
this.propagationPolicy = ExceptionPropagationPolicy.NONE;
}
可以看到contract使用的默认的Default实现,client使用的是默认的HttpURLConnection,retryer使用的是默认的Default实现等等。OpenFeign在保证了极大的扩展性的同时也保证了它的简单性。
在Builder的构造函数中,创建了InvocationHandlerFactory对象,使用的默认实现类,该对象在创建代理时,来创建代理对象的InvocationHandler。
public interface InvocationHandlerFactory {
InvocationHandler create(Target var1, Map var2);
public static final class Default implements InvocationHandlerFactory {
public Default() {
}
public InvocationHandler create(Target target, Map dispatch) {
return new FeignInvocationHandler(target, dispatch);
}
}
public interface MethodHandler {
Object invoke(Object[] var1) throws Throwable;
}
}
在配置完成后,最后调用了构造器的target方法,设置接口类对象及请求地址URL。在target方法中,我们可以发现它先创建Target对象,在调用了build方法创建ReflectiveFeign对象,最后以Target对象为参数调用Reflective的newInstance(target)方法进行初始化。
public T target(Class apiType, String url) {
return this.target(new HardCodedTarget(apiType, url));
}
public T target(Target target) {
return this.build().newInstance(target);
}
public Feign build() {
Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy);
ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
可以看到在build方法中,创建了synchronousMethodHandlerFactory工厂对象,从其名称可以知道该工厂创建的方法处理类是同步的。同时创建了ParseHandlersByName类对象,也可从名称知道该类是用来通过方法名称来解析获取Handler。
在ReflectiveFeign的newInstance(target)方法中,真正创建代理对象。通过其方法源码,来一步步解析代理对象如何创建。
public T newInstance(Target target) {
// 通过ParseHandlerByName解析接口得到方法名称和对应的MethodHandler
Map nameToHandler = this.targetToHandlersByName.apply(target);
Map methodToHandler = new LinkedHashMap();
List defaultMethodHandlers = new LinkedList();
Method[] var5 = target.type().getMethods();
int var6 = var5.length;
// 通过反射过去接口声明的方法
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
if (method.getDeclaringClass() != Object.class) {
if (Util.isDefault(method)) {
// 若接口中存在默认方法,则创建DefaultMethodHandler
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
}
// 通过默认的InvocationHandlerFactory工厂对象来创建FeignInvocationHandler对象
InvocationHandler handler = this.factory.create(target, methodToHandler);
// 再通过JDK动态代理创建代理对象
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
Iterator var12 = defaultMethodHandlers.iterator();
// 将默认方法绑定到代理对象
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
通过以上代码注释,我们可以知道创建代理对象分以下几步:
接下来,分别对每步的代码进行阅读,了解其中原理。
该方法解析生成接口声明方法对应的MethodHandler。通过源码来认识如何生成方法名对应的MethodHandler
public Map apply(Target target) {
// 首先通过配置的协议Contract来解析获取方法元数据MethodMetadata
List metadata = this.contract.parseAndValidateMetadata(target.type());
Map result = new LinkedHashMap();
Iterator var4 = metadata.iterator();
// 再根据方法元数据来分别创建不同的RequestTemplateFactory, 该对象用来resolve对应的RequestTemplate
while(var4.hasNext()) {
MethodMetadata md = (MethodMetadata)var4.next();
Object buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else {
// 以上两种BuildTemplate都继承自BuildTemplateByResolvingArgs
buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
}
if (md.isIgnored()) {
result.put(md.configKey(), (args) -> {
throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
});
} else {
// 使用SynchronousMethodHandlerFactor创建方法对应的MethodHandler
result.put(md.configKey(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
}
}
return result;
}
通过以上代码注释,我们了解到生成方法名对应的MethodHandler分为以下几个步骤:
在以上步骤中,前两步十分重要,直接关系到后续请求是否能成功进行。接下来对这两步中的源码进行了解
通过配置的Contract协议来解析方法或类上的注解,生成方法的元数据,在之前的Builder构造函数代码示例中,OpenFeign使用的是默认协议Default。通过类图我们可以了解协议类Contract之间的关系。
通过类图可以看到,Default类实现了DeclarativeContract抽象类,DeclarativeContract继承了BaseContract抽象类,BaseContract抽象类实现了Contract接口。
在Contract接口中声明了parseAndValidateMetadata方法,在BaseContract抽象类中实现了该方法,并且BaseContract抽象类声明了模板方法processAnnotationOnClass,processAnnotationOnMethod,processAnnotationsOnParameter。从名称可以看出这三个方法是分别用来处理类,方法和参数上的注解的。
在BaseContract类的parseAndValidateMetadata方法实现中,我们可以看到也是通过反射获取声明的方法Method对象,再对注解进行处理生成MethodMetadata。
public List parseAndValidateMetadata(Class> targetType) {
Util.checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", new Object[]{targetType.getSimpleName()});
Util.checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", new Object[]{targetType.getSimpleName()});
if (targetType.getInterfaces().length == 1) {
Util.checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", new Object[]{targetType.getSimpleName()});
}
Map result = new LinkedHashMap();
Method[] var3 = targetType.getMethods();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
Method method = var3[var5];
if (method.getDeclaringClass() != Object.class && (method.getModifiers() & 8) == 0 && !Util.isDefault(method)) {
MethodMetadata metadata = this.parseAndValidateMetadata(targetType, method);
Util.checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s", new Object[]{metadata.configKey()});
result.put(metadata.configKey(), metadata);
}
}
return new ArrayList(result.values());
}
在parseAndValidateMetadata(class, Method)方法中别调用了以上提到三个方法的实现,来处理类,方法和参数上的注解。
此时,我们可以看DefaultContract类做了什么。
首先,在该类中声明了三个集合:
public abstract class DeclarativeContract extends BaseContract {
private List classAnnotationProcessors = new ArrayList();
private List methodAnnotationProcessors = new ArrayList();
Map, DeclarativeContract.ParameterAnnotationProcessor> parameterAnnotationProcessors = new HashMap();
...
}
这三个集合分别存储类,方法和参数的注解处理器,而这些注解器,正是通过Default类在其构造函数中进行注册的。通过源码我们可以看到,Default中注册的注释处理器,正是我们在使用OpenFeign声明接口方法时使用的注解。
public static class Default extends DeclarativeContract {
// 该正则表达式用来匹配@RequestLine注解中声明的HTTP请求方式
static final Pattern REQUEST_LINE_PATTERN = Pattern.compile("^([A-Z]+)[ ]*(.*)$");
public Default() {
super.registerClassAnnotation(Headers.class, (header, data) -> {
String[] headersOnType = header.value();
Util.checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.", new Object[]{data.configKey()});
Map> headers = toMap(headersOnType);
headers.putAll(data.template().headers());
data.template().headers((Map)null);
data.template().headers(headers);
});
super.registerMethodAnnotation(RequestLine.class, (ann, data) -> {
String requestLine = ann.value();
Util.checkState(Util.emptyToNull(requestLine) != null, "RequestLine annotation was empty on method %s.", new Object[]{data.configKey()});
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());
}
});
// 省略其他处理器注册代码
....
}
}
通过以上源码可以看到,在使用@RequestLine注解时,必须声明HTTP的请求方式,并且为大写字母。
同时,DeclarativeContract类实现了BaseContract类的三个抽象方法 processAnnotationOnClass,processAnnotationOnMethod,processAnnotationsOnParameter。并且这些方法都以final修饰,不可被子类重写修改。在这三个方法中,使用Default注册的注解处理器,对类,方法和参数上的注解进行处理。
protected final void processAnnotationOnClass(MethodMetadata data, Class> targetType) {
Arrays.stream(targetType.getAnnotations()).forEach((annotation) -> {
this.classAnnotationProcessors.stream().filter((processor) -> {
return processor.test(annotation);
}).forEach((processor) -> {
processor.process(annotation, data);
});
});
}
protected final void processAnnotationOnMethod(MethodMetadata data, Annotation annotation, Method method) {
this.methodAnnotationProcessors.stream().filter((processor) -> {
return processor.test(annotation);
}).forEach((processor) -> {
processor.process(annotation, data);
});
}
protected final boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
Arrays.stream(annotations).filter((annotation) -> {
return this.parameterAnnotationProcessors.containsKey(annotation.annotationType());
}).forEach((annotation) -> {
((DeclarativeContract.ParameterAnnotationProcessor)this.parameterAnnotationProcessors.getOrDefault(annotation.annotationType(), DeclarativeContract.ParameterAnnotationProcessor.DO_NOTHING)).process(annotation, data, paramIndex);
});
return false;
}
至此,我们可以清晰的知道,Contract接口声明了解析方法元数据的方法parseValidateMetadata;抽象类BaseContract实现了该接口方法,并声明了处理类,方法和参数上的注解的模板方法;抽象类DeclarativeContract实现了BaseContract的模板方法,并设置了注解处理器的注册方法;Default类继承DeclarativeContract抽象类,并使用注册方法注册OpenFeign的注解处理器。在解析方法元数据时,通过Default注册的注解处理器来处理注解生成方法的元数据。
由此可以看出,OpenFeign有极大的扩展性,我们可以定制自己的注解,并重新实现DeclarativeContract抽象类来注册自己的处理器,就可以扩展注解的使用。同时,在官方文档中也说明了可以支持多种协议Contract,如Spring4,SOAP等等。
在完成方法元数据解析后,我们再回到MethodHandler创建的步骤中,接下来就是根据方法的元数据来创建不同的buildTempalte。
while(var4.hasNext()) {
MethodMetadata md = (MethodMetadata)var4.next();
Object buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else {
buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
}
....
}
以上源码说明了通过解析到的方法的元数据,为每个方法创建不同的buildTemplate对象,该对象实际是个factory对象,用来创建RequestTemplate。其中,BuildFormEncodedTemplateFromArgs和BuildEncodedTemplateFromArgs都继承自BuildTemplateByResolvingArgs类。在解析时生成RequestTemplate时,对请求参数或者请求体使用配置的Encoder进行编码。
private static class BuildEncodedTemplateFromArgs extends ReflectiveFeign.BuildTemplateByResolvingArgs {
private final Encoder encoder;
....
}
private static class BuildFormEncodedTemplateFromArgs extends ReflectiveFeign.BuildTemplateByResolvingArgs {
private final Encoder encoder;
....
}
private static class BuildTemplateByResolvingArgs implements Factory {
private final QueryMapEncoder queryMapEncoder;
protected final MethodMetadata metadata;
protected final Target> target;
private final Map indexToExpander;
....
}
interface Factory {
RequestTemplate create(Object[] var1);
}
在创建完buildTemplate对象后,以此为参数使用synchronousMethodHandlerFactory创建方法对应的MethodHandler。
result.put(md.configKey(), this.factory.create(target, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
方法的configKey在解析元数据时生成,其形式为 classSimpleName#methodName(paramTypeSimpleName,…)
在得到方法对应的MethodHandler的Map集合后,我们回到创建代理的newInstance方法中,第二步是通过反射获取接口中的方法对象,并以之前获取的Map
在该步骤中,我们重点关注的是接口中默认方法是如何处理的。
在java8中,允许接口中声明实现默认方法Default Methods,针对默认方法如何创建其对应的MethodHandler?
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
if (method.getDeclaringClass() != Object.class) {
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)));
}
}
}
通过源码可以看出,当判断方法为默认方法时,创建DefaultMethodHandler对象,在上面提到正常方法的methodHandler是通过解析的方法元数据创建的,而默认方法则是通过方法句柄来创建MethodHandler。
public DefaultMethodHandler(Method defaultMethod) {
try {
Class> declaringClass = defaultMethod.getDeclaringClass();
Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP");
field.setAccessible(true);
Lookup lookup = (Lookup)field.get((Object)null);
// 通过Lookup的unreflectSpecial创建默认方法的methodHandler
this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass);
} catch (NoSuchFieldException var5) {
throw new IllegalStateException(var5);
} catch (IllegalAccessException var6) {
throw new IllegalStateException(var6);
}
}
关于什么是方法句柄,请看以下文章进行学习了解:
《JDK1.7方法句柄学习》
《Method Handles in Java》
如何通过方法句柄调用默认方法,请看《How to explicitly invoke default method from a dynamic Proxy?》。
在创建完InvocationHandler后,进行第三步使用JDK动态代理创建代理对象。
通过JDK动态代理创建代理对象,这也是为什么要使用接口声明方法的原因。在创建完代理对象后,将默认方法的MethodHandler绑定到代理对象上。
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
Iterator var12 = defaultMethodHandlers.iterator();
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
至此,OpenFeign配置生成代理过程的源码已经阅读完毕,可以发现框架的根本就是反射和代理,这涉及到java的基础知识,反射,动态代理,方法句柄,设计模式等等。OpenFeign以其出色的设计,支持了极强的扩展性并保持了使用的简单性,非常值得我们去研究学习。
本篇博文我对OpenFeign代理对象过程中涉及到的组件扩展配置,协议解析方法元数据,MethodHandler对象的生成及最后代理对象的生成的代码进行了阅读总结,后续我将继续对执行过程中的请求生成,参数编码,结果解码,重试控制等源码进行学习总结。
参考资料:
https://blog.csdn.net/luanlouis/article/details/82821294