图解+源码讲解动态代理获取 FeignClient 代理对象

1,415 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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);
}

image.png
    返回的 client 信息如下图所示,图中的 CachingSpringLoadBalancerFacory就是要用到的负载均衡那块
image.png

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是之前定义好的同步方法处理器
image.png
image.png

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
image.png