FeignClientプロキシオブジェクトを取得するための動的プロキシを説明する図とソースコード

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して18日目です。クリックしてイベントの詳細をご覧ください。

FeignClientプロキシオブジェクトを取得するための動的プロキシを説明する図とソースコード

成功は常に一生懸命働くことを愛する人々に属します

関連記事
図+Feignがクライアントをコンテナに挿入する方法を説明するソースコード

分析を開始する場所

    クライアントの登録時にクライアントファクトリクラス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));
}
}
复制代码

    新しい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-アプリケーションタイプ是クライアント.class

protected <T> T getOptional(FeignContext context, Class<T> type) {
    return context.getInstance(this.contextId, type);
}
复制代码

image.png
    返されるクライアント情報を次の図に示します。図の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);
}
复制代码

[キーポイント]ReflectionはプロキシインスタンスReflectiveFeign#newInstanceを作成します

@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
复制代码

    nameToHandlerが処理されると、次の図のようになります。キーは、ユーザーが定義したインターフェイス名+#+メソッド名であり、値は、以前に定義された同期メソッドハンドラーです。
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;
}
复制代码

    最終的に返されるプロキシの結果は次の図で、インターフェイス名、アクセスされたサービス名、およびURLアドレスが含まれています。実際には、偽のプロキシオブジェクトReflectiveFeignが返さます
image.png

おすすめ

転載: juejin.im/post/7087756248371691550
おすすめ