写点什么

SpringCloudRPC 远程调用核心原理:FeignRPC 动态代理实例创建流程

  • 2022 年 4 月 16 日
  • 本文字数:3899 字

    阅读完需:约 13 分钟

alias = qualifier;


}


BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});


BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);


}


}


FeignClientsRegistrar 类的 registerFeignClient()方法为扫描到的每一个 RPC 客户端接口注册一个 beanDefinition 实例(Bean 的),其中的 beanClass 为 FeignClientFactoryBean。


registerFeignClient()方法的 attributes 参数值来自于 RPC 客户端接口


@FeignClient 注解所配置的值,在该方法上设置断点,在 uaa-provider 启动时可以看到的 attributes 参数的具体信息如图 3-19 所示。



图 3-19 registerFeignClient()方法的 attributes 参数值


Feign.Builder 建造者容器实例


====================


当从 Spring IOC 容器获取 RPC 接口的动态代理实例时,也就是当 FeignClientFactoryBean 的 getObject()方法被调用时,其调用的 getTarget()方法首先从 IOC 容器获取配置好的 Feign.Builder 建造者容器实例,然后通过 Feign.Builder 建造者容器实例的 target()方法完成 RPC 动态代理实例的创建。


说明


这里将 Builder 翻译为建造者,以便同构造器进行区分。


Feign.Builder 建造者容器实例在自动配置类


FeignClientsConfiguration 中完成配置,通过其源码可以看到,配置类的 feignBuilder(...)方法通过调用 Feign.builder()静态方法创建了一个建造者容器实例。


自动配置类 FeignClientsConfiguration 的部分源码如下:


package org.springframework.cloud.openfeign;


//省略 import


//Feign 客户端的配置类


@Configuration


public class FeignClientsConfiguration {


//容器实例:请求结果解码器


@Bean


@ConditionalOnMissingBean


public Decoder feignDecoder() {


return new OptionalDecoder(new ResponseEntityDecoder(


new SpringDecoder(this.messageConverters)));


}


//容器实例:请求编码器


@Bean


@ConditionalOnMissingBean


public Encoder feignEncoder() {


return new SpringEncoder(this.messageConverters);


}


//容器实例:请求重试实例,如果没有定制,就默认返回 NEVER_RETRY(不重试)实例


@Bean


@ConditionalOnMissingBean


public Retryer feignRetryer() {


return Retryer.NEVER_RETRY;


} //容器实例:Feign.Builder 客户端建造者实例,以“请求重试实例”作为参数进行初始化


@Bean


@Scope("prototype")


@ConditionalOnMissingBean


public Builder feignBuilder(Retryer retryer) {


return Feign.builder().retryer(retryer);


}


...


}


Feign.Builder 类是 feign.Feign 抽象类的一个内部类,作为 Feign 默认的建造者。Feign.Builder 类的部分源码如下:


package feign;


...


public abstract class Feign {


...


//建造者方法


public static Builder builder() {


return new Builder();


}


//内部类:建造者类


public static class Builder {


...


//创建 RPC 客户端的动态代理实例


public <T> T target(Target<T> target) {


return build().newInstance(target);


}


//建造方法


public Feign build() {


//方法处理器工厂的实例


SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =


new SynchronousMethodHandler.Factory(client,


retryer,


requestInterceptors,


logger,


logLevel, decode404);


//RPC 方法解析器


ParseHandlersByName handlersByName = new ParseHa 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 ndlersByName


(contract, options, encoder, decoder, errorDecoder, synchronous Java 开源项目【ali1024.coding.net/public/P7/Java/git】 MethodHandlerFactory);


//反射式 Feign 实例


return new ReflectiveFeign(handlersByName, invocationHandlerFactory);


}


}


当 FeignClientFactoryBean 工厂类的 getObject()方法被调用后,通过 Feign.Builder 容器实例的 target()方法完成 RPC 动态代理实例的创建。Feign.Builder 的 target()实例方法首先调用内部的 build()方法创建一个 Feign 实例,然后通过该实例的 newInstance(...)方法创建最终的 RPC 动态代理实例。默认情况下,所创建的 Feign 实例为 ReflectiveFeign 类型,二者的关系如图 3-20 所示。



图 3-20 Feign 和 ReflectiveFeign 二者之间的关系


这里通过单步断点演示一下。通过开发调试工具(如 IDEA)在 Feign.Builder 的 target(...)方法唯一的一行代码上设置一个断点,然后以调试模式启动 uaa-provider 服务,在工程启动的过程中可以看到断点所在的语句会被执行到。


断点被执行到之后,通过 IDEA 的 Evaluate 工具计算一下 target()方法运行时的 target 实参值,可以看到,它的实参值就是对 DemoClient 远程接口信息的一种二次封装,如图 3-21 所示。



图 3-21 DemoClient 动态代理实例创建时的 target()方法处的断点信息


总结一下,当从 Spring 容器获取 RPC 接口的动态代理实例时,对应的 FeignClientFactoryBean 的 getObject()方法会被调用到,然后通过 Feign.Builder 建造者容器实例的 target()方法创建 RPC 接口的动态代理实例,并缓存到 Spring IOC 容器中。


默认的 RPC 动态代理实例的创建流程


==================


默认情况下,Feign.Builder 建造者实例的 target()方法会调用自身的 build()方法创建一个 ReflectiveFeign(反射式 Feign)实例,然后调用该实例的 newInstance()方法创建远程接口最终的 JDK 动态代理实例。


ReflectiveFeign(反射式 Feign)类的实例的 newInstance()方法创建 RPC 动态代理实例的具体步骤是什么呢?先看看 ReflectiveFeign 的源码,具体如下:


package feign;


//省略 import


public class ReflectiveFeign extends Feign {


//方法解析器


private final ParseHandlersByName targetToHandlersByName;


//调用处理器工厂


private final InvocationHandlerFactory factory;


...


//创建 RPC 客户端动态代理实例


public <T> T newInstance(Target<T> target) {


//方法解析: 方法名和方法处理器的映射


Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);


//方法反射对象和方法处理器的映射


Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();


...


//创建一个 InvocationHandler 调用处理器


InvocationHandler handler = factory.create(target, methodToHandler);


//最后调用 JDK 的 Proxy.newProxyInstance 创建代理对象


T proxy = (T) Proxy.newProxyInstance(


target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);


...


//返回代理对象


return proxy;


}


终于看到 Feign 动态代理类实例的创建逻辑了,以上默认的 Feign RPC 动态代理客户端实例的创建流程和前面介绍的模拟动态代理 RPC 客户端实例的创建流程大致相似。


简单来说,默认的 Feign RPC 动态代理客户端实例的创建流程大致为以下 4 步:


(1)方法解析。解析远程接口中的所有方法,为每一个方法创建一个 MethodHandler 方法处理器,然后进行方法名称和方法处理器的 Key-Value(键-值)映射 nameToHandler。


(2)创建方法反射实例和方法处理器的映射。


通过方法名称和方法处理器的映射 nameToHandler 创建一个方法反射实例到方法处理器的 Key-Value 映射 methodToHandler,作为方法远程调用时的分发处理映射实例。


(3)创建一个 JDK 调用处理器。


主要以 methodToHandler 为参数,创建一个 InvocationHandler 调用处理器实例。


(4)创建一个动态代理对象。


调用 JDK 的 Proxy.newProxyInstance()方法创建一个动态代理实例,它的参数有 3 个:RPC 远程接口的类装载器、RPC 远程接口的 Class 实例以及上一步创建的 InvocationHandler 调用处理器实例。


远程接口的 RPC 动态代理实例的创建流程如图 3-22 所示。



图 3-22 远程接口的 RPC 动态代理实例的创建流程


以上创建 RPC 动态代理客户端实例的 4 个步骤是需要理解和掌握的重点内容,后面的介绍会根据这 4 个步骤展开。



ReflectiveFeign.newInstance()方法中首先调用了 ParseHandlersByName.apply()方法,解析 RPC 接口中的所有 RPC 方法配置(通过 Contract 解析),然后为每个 RPC 方法创建一个对应的 MethodHandler 方法处理器。


默认的 ParseHandlersByName 方法解析器是 ReflectiveFeign(反射式 Feign)类的一个内部类,它的源码如下:


package feign;


//省略 import


public class ReflectiveFeign extends Feign {


...


//内部类:方法解析器


static final class ParseHandlersByName {


//同步方法处理器工厂


private final SynchronousMethodHandler.Factory factory;


...


//RPC 接口元数据解析


public Map<String, MethodHandler> apply(Target key) {


//解析 RPC 方法元数据,返回一个方法元数据列表


List<MethodMetadata> metadata =


contract.parseAndValidatateMetadata(key.type());


Map<String, MethodHandler> result =


new LinkedHashMap<String, MethodHandler>();


//迭代 RPC 方法元数据列表


for (MethodMetadata md : metadata) {


...


//通过方法处理器工厂 factory 创建 SynchronousMethodHandler 同步方法处理实例


result.put(md.configKey(),


factory.create(key, md, buildTemplate, options, decoder, errorDecoder));


}


return result;


}


}


通过以上源码可以看到,方法解析器 ParseHandlersByName 创建方法处理器的过程是通过方法处理器工厂类实例 factory 的 create()方法完成的。而默认的方法处理器工厂类 Factory 定义在 SynchronousMethodHandler 类中,其代码如下:


package feign;


//省略 import


final class SynchronousMethodHandler implements MethodHandler {...


static class Factory {


public MethodHandler create(

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。实际上,作为程序员,丰富自己的知识储备,提升自己的知识深度和广度是很有必要的。

Mybatis 源码解析



用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
SpringCloudRPC远程调用核心原理:FeignRPC动态代理实例创建流程_Java_爱好编程进阶_InfoQ写作平台