SpringCloudRPC 远程调用核心原理:FeignRPC 动态代理实例创建流程
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(
总结
我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。实际上,作为程序员,丰富自己的知识储备,提升自己的知识深度和广度是很有必要的。
评论