Diagram + source code to explain dynamic proxy to obtain FeignClient proxy object

Get into the habit of writing together! This is the 18th day of my participation in the "Nuggets Daily New Plan·April Update Challenge", click to view the event details

Diagram + source code to explain dynamic proxy to obtain FeignClient proxy object

Success always belongs to those who love to work hard

Related articles
Diagram + source code to explain how Feign injects the client into the container

Where to start your analysis

    We registered a client factory class FeignClientFactoryBean when the client registered, so we obtained the FeignClient designation from this factory, so let's see how to get the FeignClient, generally when the factory is used, then The getObject method is the method to get the instance

@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);
复制代码

    What things are constructed in this constructor, enter the constructor

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;
}
复制代码

    Continue to look at the process behind the getTarget method, splicing the url into the address 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));
}
}
复制代码

    In the new HardCodedTarget method, there is nothing special about the assignment. Just look at the loadBalance method.

loadBalance() method

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 operation

    this.contextId 是 GOODS-APPLICATION,type 是 Client.class

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

image.png
    The returned client information is shown in the figure below. The CachingSpringLoadBalancerFacory in the figure is the load balancing piece to be used.
image.png

HystrixTargeter#target method

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 method

    The build method actually creates a reflection object about feign, and then calls the newInstance method

public <T> T target(Target<T> target) {
  // 创建了一个 ReflectiveFeign 对象,之后调用 ReflectiveFeign#newInstance 方法
  return build().newInstance(target);
}
复制代码

    First enter the build method and see what operations are done in this method. In fact, it mainly returns a ReflectiveFeign object. In fact, it is related to reflection at first glance. Let's focus on the ReflectiveFeign#newInstance method.

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);
}
复制代码

[Key points] Reflection creates a proxy instance ReflectiveFeign#newInstance

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

    After nameToHandler is processed, it will look like the picture below. The key is the interface name + # + method name defined by us , and the value is the previously defined synchronization method handler.
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;
}
复制代码

    The final returned proxy result is the following figure, which contains the interface name, the accessed service name, and the url address. In fact, it returns a feign proxy object ReflectiveFeign
image.png

Guess you like

Origin juejin.im/post/7087756248371691550