基于代理模式构建Feign.Builder

FeignClientFactoryBean

上一篇文章中,我们完成了Feign相关包以及组件扫描相关源码的阅读,这一节开始进行FeignClient相关组件的初始化,即通过代理模式生成相关的Bean.入口类为FeignClientFactoryBean,本篇主要讲解如下内容

目录

  • 完成Feign.Builder的构建
  • 构建动态代理与LoadBalancerFeignClient

1.动态代理构建Feign.Builder

FeignClientFactoryBean

1.1.核心入口方法

和阅读其他源码的套路一样,我们先看@Override相关的方法,找到如下

  1. @Override
  2. public Object getObject() throws Exception {
  3. return getTarget();
  4. }

下面我们来看这个核心方法的实现,代码片段如下

  1. <T> T getTarget() {
  2. FeignContext context = this.applicationContext.getBean(FeignContext.class);
  3. Feign.Builder builder = feign(context);
  4. //因为没有在@FeignClient中指定url,因此会走下面的逻辑
  5. if (!StringUtils.hasText(this.url)) {
  6. if (!this.name.startsWith("http")) {
  7. this.url = "http://" + this.name;
  8. }
  9. else {
  10. this.url = this.name;
  11. }
  12. this.url += cleanPath();
  13. return (T) loadBalance(builder, context,
  14. new HardCodedTarget<>(this.type, this.name, this.url));
  15. }
  16. .....
  17. }

FeignContext

FeignAutoConfiguration中找到了FeignContext的初始化

  1. @Bean
  2. public FeignContext feignContext() {
  3. FeignContext context = new FeignContext();
  4. context.setConfigurations(this.configurations);
  5. return context;
  6. }

FeignContext内部维护了一个map,key为服务名,value是对应的spring容器
初始化的时候分别将decoder,encoder,logger等组件全部放到该map中

1.2.构建Feign.Builder

feign(context);

  1. protected Feign.Builder feign(FeignContext context) {
  2. FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  3. Logger logger = loggerFactory.create(this.type);//创建Slf4jLogger
  4. // @formatter:off
  5. Feign.Builder builder = get(context, Feign.Builder.class)
  6. // required values
  7. .logger(logger)
  8. .encoder(get(context, Encoder.class))//从FeignContext中获取ResponseEntityDecoder
  9. .decoder(get(context, Decoder.class))
  10. .contract(get(context, Contract.class));
  11. // @formatter:on
  12. //加载配置文件
  13. configureFeign(context, builder);
  14. return builder;
  15. }
  • FeignContext中获取到Feign.Builder对应的组件
    • logger:Slf4jLogger,通过Factory创建
    • Encoder:SpringEncoder
    • Decoder
    • Contract
  • 读取配置文件,优先级从低到高
    • 自定义配置类,若有,低
    • default配置,中
    • 指定服务的配置: 高

3.基于代理模式构建Feign.Builder - 图1

2.基于Feign.Builder构建FeignClient

这里创建的是LoadBalancerFeignClient

HardCodedTarget

  1. public static class HardCodedTarget<T> implements Target<T> {
  2. private final Class<T> type;
  3. private final String name;
  4. private final String url;
  5. }

在我们这里的HardCodedTarget的属性分别是

  • type: OrderClient,即对于的FeignClient
  • name: SERVICE-ORDER
  • url: http://SERVICE-ORDER
Client client = getOptional(context, Client.class);

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

从FeignContext中获取到LoadBalancerFeignClient,之后将该client设置到builder中

3.基于代理模式构建Feign.Builder - 图2

这一步我们完成了图中最下面这部分逻辑,即将服务名URL,对应的client类型构建一个Client->LoadBalancerFeignClient,绑定到Feign.Builder上

2.创建动态代理对象的细节

HystrixTargeter.target()

    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
            return feign.target(target);
        }

    }

这里的Builder是Feign.Builder,因此会走上面的逻辑.最终通过feign.target(target)走如下流程

  public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    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 {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    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;
  }

2.1.基于SpringMVCContract解析方法的定义

第一行代码 Map nameToHandler = targetToHandlersByName.apply(target); 该方法主要是将FeignClient中对应的多个请求接口转化为MethodHandler

    public Map<String, MethodHandler> apply(Target target) {
      List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate =
              new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
        }
        if (md.isIgnored()) {
          result.put(md.configKey(), args -> {
            throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
          });
        } else {
          result.put(md.configKey(),
              factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
        }
      }
      return result;
    }
  }

3.基于代理模式构建Feign.Builder - 图3

3.基于代理模式构建Feign.Builder - 图4

其中MethodHandler中包含的内容如上,

  • key即为对应FeignClient中的请求接口,
  • Client是和Ribbon整合的一个接口调用组件,即LoadBalancerFeignClient
  • 这里创建处理的MethodHandlerSynchronousMethodHandler

关于