一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情
图解+源码讲解 动态代理获取 FeignClient 代理对象
成功永远属于那些爱拼搏的人
feign 相关文章
图解+源码讲解 Feign 如何将客户端注入到容器中
图解+源码讲解动态代理获取 FeignClient 代理对象
图解+源码讲解代理对象 ReflectiveFeign 分析
图解+源码讲解 Feign 如何选取指定服务
图解+源码讲解 Feign 请求的流程
ribbon 相关文章
图解+源码讲解 Ribbon 如何获取注册中心的实例
图解+源码讲解 Ribbon 原理初探
图解+源码讲解 Ribbon 服务列表更新
图解+源码讲解 Ribbon 服务选择原理
Ribbon 原理初探
eureka 相关文章
eureka-server 项目结构分析
图解+源码讲解 Eureka Server 启动流程分析
图解+源码讲解 Eureka Client 启动流程分析
图解+源码讲解 Eureka Server 注册表缓存逻辑
图解+源码讲解 Eureka Client 拉取注册表流程
图解+源码讲解 Eureka Client 服务注册流程
图解+源码讲解 Eureka Client 心跳机制流程
图解+源码讲解 Eureka Client 下线流程分析
图解+源码讲解 Eureka Server 服务剔除逻辑
图解+源码讲解 Eureka Server 集群注册表同步机制
从哪里开始分析
我们在客户端进行注册的时候注册了一个客户端工厂类 FeignClientFactoryBean ,所以我们获取 FeignClient 指定是从这个工厂里面获取的了,所以我们就看看是怎么获取的这个FeignClient,一般用到了工厂的时候那么getObject方法就是获取实例的方法
@Override
public Object getObject() throws Exception {
// 获取目标实例
return getTarget();
}
getTarget()
// 返回一个 feign构造器客户端,通过 feingContext
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// 获取 feign 构造器
Feign.Builder builder = feign(context);
这个构造器里面都构造了哪些东西呢,进入到构造器中
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
configureFeign(context, builder);
return builder;
}
继续看 getTarget 方法的后面的流程,将 url 拼接成 http://GOODS-APPLICATION 这个地址
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
// 进行url拼接
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
// http://GOODS-APPLICATION
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
}
new HardCodedTarget 这个方法里面就是进行了赋值没有什么特殊的东西,直接看 loadBalance 这个方法
loadBalance() 方法
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// 根据feignCotext 上下文获取客户端实例,获取的client是 LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
// 如果客户端不为空
if (client != null) {
// 进行客户端创建
builder.client(client);
// 获取 Targeter 操作,这个Targeter 是 HystrixTargerer
Targeter targeter = get(context, Targeter.class);
// 调用 Targeter 的target方法
return targeter.target(this, builder, context, target);
}
}
getOptional 操作
this.contextId 是 GOODS-APPLICATION,type 是 Client.class
protected <T> T getOptional(FeignContext context, Class<T> type) {
return context.getInstance(this.contextId, type);
}
返回的 client 信息如下图所示,图中的 CachingSpringLoadBalancerFacory就是要用到的负载均衡那块
HystrixTargeter#target 方法
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 它两不是一种类型所以就是false 之后就是true,进入feign#target方法
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
......
}
Feign.Builder#target 方法
build 方法里面其实就是创建了一个有关于feign的反射对象,之后又调用了 newInstance 方法
public <T> T target(Target<T> target) {
// 创建了一个 ReflectiveFeign 对象,之后调用 ReflectiveFeign#newInstance 方法
return build().newInstance(target);
}
先进入build 方法,看看这个方法里面做了什么操作,其实里面主要是做了返回一个 ReflectiveFeign对象,其实一看就是和反射有关系,我们重点看一下ReflectiveFeign#newInstance方法
public Feign build() {
// 同步方法处理工厂,这里面构建了一些信息
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
logger, logLevel, decode404, closeAfterDecode, propagationPolicy);
// 解析一些信息
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
// 重要的方法new了一个 ReflectiveFeign
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
【重点】反射创建代理实例 ReflectiveFeign#newInstance
@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
nameToHandler 处理完就是下图的样子,key是咱们定义的接口名字+#+方法名字,value是之前定义好的同步方法处理器
Map<Method, MethodHandler> methodToHandler =
new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers =
new LinkedList<DefaultMethodHandler>();
// 遍历 com.shangde.springcloud.service.GoodsClient 的所有方法,这里面就一个方法 goods
// method public abstract org.springframework.http.ResponseEntity
// com.shangde.springcloud.service.GoodsClient.goods()
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
// 因为它的声明不是类也不是默认工具方法所以直接走这里
// Feign.configKey(target.type(), method)) 是 GoodsClient#goods()
// 获取的 MethodHandler 就是 SynchronousMethodHandler
methodToHandler.put(method,
nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 创建动态代理,target 是HardCodeTarget,MethodHandler 就是 SynchronousMethodHandler
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
最后返回的proxy结果就是下图,里面有接口名字,访问的服务名字,以及url地址,其实就是返回了一个feign的代理对象 ReflectiveFeign