阿亮的日志

  • 首页
  • 分类
  • 归档
  • 关于

  • 搜索
事务 feign b 有序性 原子性 可见性 volatile 并发编程 hystrix 源码 eureka springcloud JVM

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

发表于 2021-03-11 | 分类于 Feign源码解读 | 0 | 阅读次数 37

基于代理模式构建Feign.Builder

FeignClientFactoryBean

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

目录

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

1.动态代理构建Feign.Builder

FeignClientFactoryBean

1.1.核心入口方法

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

@Override
public Object getObject() throws Exception {
    return getTarget();
}

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

 <T> T getTarget() {
  FeignContext context = this.applicationContext.getBean(FeignContext.class);
  Feign.Builder builder = feign(context);
        //因为没有在@FeignClient中指定url,因此会走下面的逻辑
  if (!StringUtils.hasText(this.url)) {
   if (!this.name.startsWith("http")) {
    this.url = "http://" + this.name;
   }
   else {
    this.url = this.name;
   }
   this.url += cleanPath();
   return (T) loadBalance(builder, context,
     new HardCodedTarget<>(this.type, this.name, this.url));
  }
        .....
 }

FeignContext

在FeignAutoConfiguration中找到了FeignContext的初始化

@Bean
public FeignContext feignContext() {
    FeignContext context = new FeignContext();
    context.setConfigurations(this.configurations);
    return context;
}

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

1.2.构建Feign.Builder

feign(context);

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

2.基于Feign.Builder构建FeignClient

这里创建的是LoadBalancerFeignClient

HardCodedTarget

  public static class HardCodedTarget<T> implements Target<T> {
    private final Class<T> type;
    private final String name;
    private final String url;

  } 

在我们这里的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中

这一步我们完成了图中最下面这部分逻辑,即将服务名,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<String, MethodHandler> 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;
    }
  }

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

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

关于

  • Github: https://github.com/liangliang1259/common-notes
  • 公众号
坚持有质量的创作,您的支持将支持我继续创作!
阿亮 微信支付

微信支付

阿亮 支付宝

支付宝

  • 本文作者: 阿亮
  • 本文链接: http://sunliangliang.com/?p=41
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 事务 # feign # b # 有序性 # 原子性 # 可见性 # volatile # 并发编程 # hystrix # 源码 # eureka # springcloud # JVM
02.Feign的核心加载流程
04.Feign请求处理流程
  • 文章目录
  • 站点概览
阿亮

阿亮

26 日志
11 分类
13 标签
RSS
Github E-mail
Creative Commons
0%
© 2021 阿亮
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4