博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenFeign学习(三):OpenFeign配置生成代理对象
阅读量:4096 次
发布时间:2019-05-25

本文共 17184 字,大约阅读时间需要 57 分钟。

目录结构

说明

在之前的两篇博文和中,我简单介绍了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;}

通过以上代码注释,我们可以知道创建代理对象分以下几步:

  • 通过ParseHandlerByName的apply()方法获取方法名称对应的MethodHandler
  • 通过反射得到接口声明方法,并得到Method对象对应的MethodHandler生成Map集合nameToHandler,并使用invocationHandlerFactory创建InvocationHandler对象
  • 通过JDK动态代理创建代理对象
  • 将接口的默认方法绑定到代理对象

接下来,分别对每步的代码进行阅读,了解其中原理。

ParseHandlerByName.apply(target)

该方法解析生成接口声明方法对应的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解析获取方法的元数据MethodMetadata
  • 根据MethodMetadata创建每个方法对应的buildTemplate对象,该对象在请求时构造生成不同的requestTemplate
  • 对于每个方法,使用SynchronousMethodHandlerFactor创建对应的MethodHandler。并以方法元数据configKey为键,MethodHandler为值存储返回

在以上步骤中,前两步十分重要,直接关系到后续请求是否能成功进行。接下来对这两步中的源码进行了解

contract.parseAndValidateMetadata

通过配置的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。

BuildTemplateByResolvingArgs

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。

synchronousMethodHandlerFactory.create

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<configKey, MethodHandler> map为依据,获取Map<Method, MethodHandler> nameToHandler的map集合。生成代理对象使用的InvocationHandler对象。

InvocationHandler

在该步骤中,我们重点关注的是接口中默认方法是如何处理的。

在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); }}

关于什么是方法句柄,请看以下文章进行学习了解:

如何通过方法句柄调用默认方法,请看。

在创建完InvocationHandler后,进行第三步使用JDK动态代理创建代理对象。

Proxy.newProxyInstance

通过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

你可能感兴趣的文章
[单片机]51单片机存储器结构
查看>>
[操作系统]锁的实现机制
查看>>
[系统分析]面向对象思考方式
查看>>
[系统分析]UML建模
查看>>
[JAVA]无ide手动编译java文件
查看>>
[信息安全]密码学基础(一)
查看>>
[信息安全]密码学基础(二)
查看>>
[JAVA]码农翻身要点记录
查看>>
VS2015 学习笔记(一):新建C/C++项目、程序调试以及部分头文件缺失及其他小问题的解决办法
查看>>
After Effects(AE) 基本操作(一)
查看>>
FFmpeg学习笔记(一):FFmpeg在win 7 下的安装与使用
查看>>
FFmpeg学习笔记(二):FFmpeg指令学习
查看>>
Direct3D(D3D)简介
查看>>
DirectX 10 SDK在VS 2010下的安装配置
查看>>
FFmpeg学习笔记(三):逐行扫描转换为隔行扫描的实现----tinterlace简介
查看>>
Matlab运行时出现“Out of Memory”问题,可能的解决办法总结
查看>>
在使用Matlab过程中遇到的问题及其可能的解决办法
查看>>
Matlab中函数fopen、fread、fseek和fwrite的用法
查看>>
SSE指令集学习
查看>>
C++学习笔记(一):打开文件、读取数据、数据定位与数据写入
查看>>