一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・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);
}
复制代码
返されるクライアント情報を次の図に示します。図の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);
}
复制代码
[キーポイント]ReflectionはプロキシインスタンスReflectiveFeign#newInstanceを作成します
@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
复制代码
nameToHandlerが処理されると、次の図のようになります。キーは、ユーザーが定義したインターフェイス名+#+メソッド名であり、値は、以前に定義された同期メソッドハンドラーです。
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が返されます。