本文共 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, Mapvar2); 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)方法进行初始化。
publicT 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)方法中,真正创建代理对象。通过其方法源码,来一步步解析代理对象如何创建。
publicT 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 Mapapply(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 ListparseAndValidateMetadata(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 ListclassAnnotationProcessors = 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 MapindexToExpander; ....}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<configKey, MethodHandler> map为依据,获取Map<Method, MethodHandler> nameToHandler的map集合。生成代理对象使用的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动态代理创建代理对象。
通过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