0. 环境
- nacos版本:1.4.1
- Spring Cloud : Hoxton.SR9(没用2020.0.2版本后面说明)
- Spring Boot :2.4.4
- Spring Cloud alibaba: 2.2.5.RELEASE
- Spring Cloud openFeign 2.2.2.RELEASE
测试代码:github.com/hsfxuebao/s…
2020.0.X版本开始的OpenFeign底层不再使用Ribbon了
1. FeignClient的bean注册过程

1.1 @EnableFeignClients
  想要集成 Feign 客户端,需要我们通过注解 @EnableFeignClients 来开启。这个注解开启了FeignClient的解析过程。这个注解的声明如下,它用到了一个@Import注解,我们知道Import是用来导入一个配置类的,接下来去看一下FeignClientsRegistrar的定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
复制代码FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,它是一个动态注入bean的接口,Spring Boot启动的时候,会去调用这个类中的registerBeanDefinitions来实现动态Bean的装载。它的作用类似于ImportSelector。
  然后就会进入 FeignClientsRegistrar# registerBeanDefinitions 。registerDefaultConfiguration 方法内部从 SpringBoot 启动类上检查是否有@EnableFeignClients, 有该注解的话, 则完成 Feign 框架相关的一些配置内容注册registerFeignClients 方法内部从 classpath 中, 扫描获得 @FeignClient修饰的类, 将类的内容解析为 BeanDefinition , 最终通过调用 Spring 框架中的BeanDefinitionReaderUtils.resgisterBeanDefinition 将解析处理过的 FeignClientBeanDeifinition 添加到 spring 容器中.
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
   // todo 将@EnableFeignClients注解中的defaultConfiguration属性注册到一个缓存map
   registerDefaultConfiguration(metadata, registry);
   // todo 1) 扫描所有@FeignClient注解的接口,即扫描到所有Feign接口
   // 2) 将每个@FeignClient注解的configuration属性注册到一个缓存map
   // 3) 根据@FeignClient注解元数据生成 FeignClientFactoryBean 的BeanDefinition,
   //    并将这个BeanDefinition注册到一个缓存map
   registerFeignClients(metadata, registry);
}
复制代码- registerDefaultConfiguration:- private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 获取到@EnableFeignClients注解的属性值 // 第二个参数true,表示将Class类型的属性变为了String类型 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; // 判断当前注解所标注的类是否为闭合类 if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } // todo 注册这个defaultConfiguration属性 registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } } private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { // 生成一个Builder BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); // 获取到构建的BeanDefinition } 复制代码
- registerFeignClients- public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 定义一个扫描器 ClassPathScanningCandidateComponentProvider scanner = getScanner(); // 初始化扫描器 scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; // 获取@EnableFeignClients注解的属性元数据 Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); // 获取clients属性值 final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); // 若clients属性为空,则启用类路径扫描 if (clients == null || clients.length == 0) { // 为扫描器添加一个扫描@FeignClient注解的Filter scanner.addIncludeFilter(annotationTypeFilter); // todo 获取@EnableFeignClients注解中所有指定的基本包 basePackages = getBasePackages(metadata); } else { // 指定了clients属性,将所有指定的Feign接口类添加到集合 final Set<String> clientClasses = new HashSet<>(); basePackages = new HashSet<>(); for (Class<?> clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() { @Override protected boolean match(ClassMetadata metadata) { String cleaned = metadata.getClassName().replaceAll("\$", "."); return clientClasses.contains(cleaned); } }; scanner.addIncludeFilter( new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter))); } // 遍历这些基本包,将其它的@FeignClient接口找到并写入到集合 for (String basePackage : basePackages) { Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage); // 遍历 候选组件 集合(所有Feign接口都在这个集合中) for (BeanDefinition candidateComponent : candidateComponents) { // 只处理Feign接口组件 if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; // 从BeanDefinition中获取注解元数据 AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); // 断言:若不是接口,直接抛出异常 Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); // 获取@FeignClient注解属性 Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); // todo 获取Feign名称 String name = getClientName(attributes); // 将当前遍历的Feign接口的configuration注册到缓存map registerClientConfiguration(registry, name, attributes.get("configuration")); // 将FeignClientFactoryBean的BeanDefinition注册到缓存map registerFeignClient(registry, annotationMetadata, attributes); } } } } 复制代码- registerFeignClient在这个方法中,就是去组装BeanDefinition,也就是Bean的定义,然后注册到- Spring IOC容器。
 我们关注一下,BeanDefinitionBuilder是用来构建一个BeanDefinition的,它是通过- private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { // 获取FeignClient接口的类全路径 String className = annotationMetadata.getClassName(); // 生成一个beanFactory,其会为FeignClientFactoryBean生成一些必要的组件 BeanDefinitionBuilder definition = BeanDefinitionBuilder .genericBeanDefinition(FeignClientFactoryBean.class); validate(attributes); definition.addPropertyValue("url", getUrl(attributes)); definition.addPropertyValue("path", getPath(attributes)); String name = getName(attributes); definition.addPropertyValue("name", name); String contextId = getContextId(attributes); definition.addPropertyValue("contextId", contextId); definition.addPropertyValue("type", className); definition.addPropertyValue("decode404", attributes.get("decode404")); definition.addPropertyValue("fallback", attributes.get("fallback")); definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory")); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); String alias = contextId + "FeignClient"; // 获取到FeignClientFactoryBean的beanDefinition AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be null beanDefinition.setPrimary(primary); String qualifier = getQualifier(attributes); if (StringUtils.hasText(qualifier)) { alias = qualifier; } // 生成beanDefinition的holder,通过holder可以获取到这个beanDefinition BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias }); // 把BeanDefinition的这个bean定义注册到IOC容器 BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); } 复制代码- genericBeanDefinition来构建的,并且传入了一个FeignClientFactoryBean的类,代码如下。- public static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass) { BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition()); builder.beanDefinition.setBeanClass(beanClass); return builder; } 复制代码
1.2 小节
综上代码分析,其实实现逻辑很简单。
- 创建一个BeanDefinitionBuilder。
- 创建一个工厂Bean,并把从@FeignClient注解中解析的属性设置到这个FactoryBean中
- 调用registerBeanDefinition注册到IOC容器中
2. 动态代理过程详解

我们可以发现,FeignClient被动态注册成了一个FactoryBean.
Spring Cloud FengnClient实际上是利用Spring的代理工厂来生成代理类,所以在这里地方才会把所有的FeignClient的BeanDefinition设置为FeignClientFactoryBean类型,而FeignClientFactoryBean继承自FactoryBean,它是一个工厂Bean。在Spring中,FactoryBean是一个工厂Bean,用来创建代理Bean。工厂 Bean 是一种特殊的 Bean, 对于 Bean 的消费者来说, 他逻辑上是感知不到这个 Bean 是普通的 Bean 还是工厂 Bean, 只是按照正常的获取 Bean 方式去调用, 但工厂bean 最后返回的实例不是工厂Bean 本身, 而是执行工厂 Bean 的 getObject 逻辑返回的示例。
  简单来说,FeignClient标注的这个接口,会通过FeignClientFactoryBean.getObject()这个方法获得一个代理对象。
2.1 FeignClientFactoryBean.getObject
  getObject调用的是getTarget方法,它从applicationContext取出FeignContext,FeignContext继承了NamedContextFactory,它是用来统一维护feign中各个feign客户端相互隔离的上下文。
springboot启动时会加载类路径下/META-INF/spring.factories中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类,FeignContext注册到容器是在FeignAutoConfiguration上完成的。FeignContext是全局唯一的上下文,它继承了NamedContextFactory,它是用来统一维护feign中各个feign客户端相互隔离的上下文。

@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
    FeignContext context = new FeignContext();
    context.setConfigurations(this.configurations);
    return context;
}
复制代码  在初始化FeignContext时,会把configurations在容器中放入FeignContext中。configurations 的来源就是在前面registerFeignClients方法中将@FeignClient的配置 configuration。
  接着,构建feign.builder,在构建时会向FeignContext获取配置的Encoder,Decoder等各种信息。FeignContext在上文中已经提到会为每个Feign客户端分配了一个容器,它们的父容器就是spring容器。
  配置完Feign.Builder之后,再判断是否需要LoadBalance,如果需要,则通过LoadBalance的方法来设置。实际上他们最终调用的是Target.target()方法。
@Override
public Object getObject() throws Exception {
   return getTarget();
}
/**
 * @param <T> the target type of the Feign client
 * @return a {@link Feign} client created with the specified data and the context
 * information
 */
<T> T getTarget() {
   // 实例化Feign上下文对象FeignContext
   FeignContext context = this.applicationContext.getBean(FeignContext.class);
   // todo 从Spring子容器中获取相应的实例
   Feign.Builder builder = feign(context);
   // 若url属性为空,则说明其要采用负载均衡方式调用提供者,生成有负载均衡功能的代理类
   if (!StringUtils.hasText(this.url)) {
      // 若name属性不以http开头
      if (!this.name.startsWith("http")) {
         this.url = "http://" + this.name;
      }
      else {
         this.url = this.name;
      }
      // 将规范化的path属性连接到url后
      // 规范化path:若path属性不以斜杠(/)开头,则为其添加斜杠,以斜杠结尾,则去掉尾部斜杠
      this.url += cleanPath();
      // 负载均衡调用
      return (T) loadBalance(builder, context,
            new HardCodedTarget<>(this.type, this.name, this.url));
   }
   // 这是url不为空的情况,即采用直连方式访问提供者,则生成默认的代理类
   if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
      this.url = "http://" + this.url;
   }
   String url = this.url + cleanPath();
   // 从Spring子容器中获取Client
   Client client = getOptional(context, Client.class);
   if (client != null) {
      if (client instanceof LoadBalancerFeignClient) {
         // not load balancing because we have a url,
         // but ribbon is on the classpath, so unwrap
         client = ((LoadBalancerFeignClient) client).getDelegate();
      }
      if (client instanceof FeignBlockingLoadBalancerClient) {
         // not load balancing because we have a url,
         // but Spring Cloud LoadBalancer is on the classpath, so unwrap
         client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
      }
      builder.client(client);
   }
   // todo 生成默认的代理类
   Targeter targeter = get(context, Targeter.class);
   return (T) targeter.target(this, builder, context,
         new HardCodedTarget<>(this.type, this.name, url));
}
复制代码Feign.Builder用来构建Feign对象,基于builder实现上下文信息的构建,代码如下。
protected Feign.Builder feign(FeignContext context) {
    // todo
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(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)); //contract协议,用来实现模版解析(后面再详细分析)
    // @formatter:on
    configureFeign(context, builder);
    applyBuildCustomizers(context, builder);
    return builder;
}
复制代码从代码中可以看到,feign方法,主要是针对不同的服务提供者生成Feign的上下文信息,比如logger、encoder、decoder等。因此,从这个分析过程中,我们不难猜测到它的原理结构,如下图所示:

父子容器隔离的实现方式如下,当调用get方法时,会从context中去获取指定type的实例对象。
//FeignContext.java
protected <T> T get(FeignContext context, Class<T> type) {
    T instance = context.getInstance(contextId, type);
    if (instance == null) {
        throw new IllegalStateException(
            "No bean found of type " + type + " for " + contextId);
    }
    return instance;
}
复制代码接着,调用NamedContextFactory中的getInstance方法:
//NamedContextFactory.java
public <T> T getInstance(String name, Class<T> type) {
    //根据`name`获取容器上下文
    AnnotationConfigApplicationContext context = this.getContext(name);
    try {
        //再从容器上下文中获取指定类型的bean。
        return context.getBean(type);
    } catch (NoSuchBeanDefinitionException var5) {
        return null;
    }
}
复制代码getContext方法根据name从contexts容器中获得上下文对象,如果没有,则调用createContext创建。
protected AnnotationConfigApplicationContext getContext(String name) {
    if (!this.contexts.containsKey(name)) {
        synchronized(this.contexts) {
            if (!this.contexts.containsKey(name)) {
                this.contexts.put(name, this.createContext(name));
            }
        }
    }
    return (AnnotationConfigApplicationContext)this.contexts.get(name);
}
复制代码2.2 生成动态代理
loadBalance :生成具备负载均衡能力的feign客户端,为feign客户端构建起绑定负载均衡客户端。
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
      HardCodedTarget<T> target) {
    //Feign发送请求以及接受响应的http client,默认是Client.Default的实现,可以修改成OkHttp、HttpClient等。
   Client client = getOptional(context, Client.class);
   if (client != null) {
      builder.client(client);//针对当前Feign客户端,设置网络通信的client
      //targeter表示HystrixTarger实例,因为Feign可以集成Hystrix实现熔断,所以这里会一层包装。
      Targeter targeter = get(context, Targeter.class);
      return targeter.target(this, builder, context, target);
   }
   throw new IllegalStateException(
         "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
复制代码Client client = (Client)this.getOptional(context, Client.class); 从上下文中获取一个 Client,默认是LoadBalancerFeignClient。它是在FeignRibbonClientAutoConfiguration这个自动装配类中,通过Import实现的
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
        OkHttpFeignLoadBalancedConfiguration.class,
        DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
  .....
}
复制代码  这里的通过 DefaultFeignLoadBalancedConfiguration 注入客户端 Client 的实现
@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
            SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
                clientFactory);
    }
}
复制代码  接下去进入 targeter.target(this, builder, context, target) ,携带着构建好的这些对象去创建代理实例 ,这里有两个实现 HystrixTargeter 、DefaultTargeter 很显然,我们没有配置 Hystrix ,这里会走 DefaultTargeter
class DefaultTargeter implements Targeter {
    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
            FeignContext context, Target.HardCodedTarget<T> target) {
        return feign.target(target);
    }
}
复制代码  然后会来到 feign.Feign.Builder#target(feign.Target)
public <T> T target(Target<T> target) {
      return build().newInstance(target);
}
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);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
复制代码  最终会调用 ReflectiveFeign.newInstance
这个方法是用来创建一个动态代理的方法,在生成动态代理之前,会根据Contract协议(协议解析规则,解析接口类的注解信息,解析成内部的MethodHandler的处理方式。
  从实现的代码中可以看到熟悉的Proxy.newProxyInstance方法产生代理类。而这里需要对每个定义的接口方法进行特定的处理实现,所以这里会出现一个MethodHandler的概念,就是对应方法级别的InvocationHandler。
public <T> T newInstance(Target<T> target) {
    // 解析接口注解信息
    //根据接口类和Contract协议解析方式,解析接口类上的方法和注解,转换成内部的MethodHandler处理方式
    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);
  // 基于Proxy.newProxyInstance 为接口类创建动态实现,将所有的请求转换给InvocationHandler 处理。
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}
复制代码  targetToHandlersByName.apply(target) :根据Contract协议规则,解析接口类的注解信息,解析成内部表现:targetToHandlersByName.apply(target);会解析接口方法上的注解,从而解析出方法粒度的特定的配置信息,然后生产一个SynchronousMethodHandler 然后需要维护一个<method,MethodHandler>的map,放入InvocationHandler的实现FeignInvocationHandler中。
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;
}
复制代码2.3 小节
上述代码,其实也不难理解。
-  解析@FeignClient接口声明的方法,根据不同方法绑定不同的处理器。 - 默认方法,绑定DefaultMethodHandler
- 远程方法,绑定SynchronousMethodHandler
 
- 默认方法,绑定
-  使用JDK提供的Proxy创建动态代理 
MethodHandler,会把方法参数、方法返回值、参数集合、请求类型、请求路径进行解析存储,如下图所示。

-  被声明为 @FeignClient注解的类,在被注入时,最终会生成一个动态代理对象FeignInvocationHandler。当触发方法调用时,会被 FeignInvocationHandler#invoke拦截,FeignClientFactoryBean在实例化过程中所做的事情如下图所示。 
总结来说就几个点:
- 解析Feign的上下文配置,针对当前的服务实例构建容器上下文并返回Feign对象
- Feign根据上下围配置把 log、encode、decoder、等配置项设置到Feign对象中
- 对目标服务,使用LoadBalance以及Hystrix进行包装
- 通过Contract协议,把FeignClient接口的声明,解析成MethodHandler
- 遍历MethodHandler列表,针对需要远程通信的方法,设置SynchronousMethodHandler处理器,用来实现同步远程调用。
- 使用JDK中的动态代理机制构建动态代理对象。
3. OpenFeign调用过程
OpenFeign调用过程图示:

 在前面的分析中,我们知道OpenFeign最终返回的是一个 ReflectiveFeign.FeignInvocationHandler 的对象。那么当客户端发起请求时,会进入到 FeignInvocationHandler.invoke 方法中,这个大家都知道,它是一个动态代理的实现。
@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }
      // todo 利用分发器筛选方法,找到对应的handler 进行处理
      return dispatch.get(method).invoke(args);
}
复制代码而接着,在invoke方法中,会调用 this.dispatch.get(method)).invoke(args) 。this.dispatch.get(method) 会返回一个SynchronousMethodHandler,进行拦截处理。这个方法会根据参数生成完成的RequestTemplate对象,这个对象是Http请求的模版,代码如下。
@Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        // todo
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
}
复制代码  经过上述的代码,我们已经将restTemplate拼装完成,上面的代码中有一个 executeAndDecode() 方法,该方法通过RequestTemplate生成Request请求对象,然后利用Http Client获取response,来获取响应信息。
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    //转化为Http请求报文
    Request request = targetRequest(template);
    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }
     
    Response response;
    long start = System.nanoTime();
    try {
      //发起远程通信
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      //获取返回结果
     
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
    // .......
}
复制代码  经过上面的分析,这里的 client.execute 的 client 的类型是LoadBalancerFeignClient,这里就很自然的进入 LoadBalancerFeignClient#execute。
public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);
            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName)
                    .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
}
复制代码  其实这个execute里面得流程就是 Ribbon 的那一套。我们可以简单的看一下。首先是构造URI,构造RibbonRequest,选择 LoadBalance,发起调用。
  来看一下lbClient 选择负载均衡器的时候做了什么
public FeignLoadBalancer create(String clientName) {
        FeignLoadBalancer client = this.cache.get(clientName);
        if (client != null) {
            return client;
        }
        IClientConfig config = this.factory.getClientConfig(clientName);
        ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
        ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
                ServerIntrospector.class);
        client = this.loadBalancedRetryFactory != null
                ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
                        this.loadBalancedRetryFactory)
                : new FeignLoadBalancer(lb, config, serverIntrospector);
        this.cache.put(clientName, client);
        return client;
}
复制代码  可以得出的结论就是 this.factory.getLoadBalancer(clientName) 跟Ribbon 源码里的获取方式一样,无疑这里获取的就是默认的 ZoneAwareLoadBalancer。然后包装成一个 FeignLoadBalancer 进行返回
既然负载均衡器选择完了,那么一定还有个地方通过该负载去选择一个服务,接着往下看:
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
        try {
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
            Throwable t = e.getCause();
            if (t instanceof ClientException) {
                throw (ClientException) t;
            } else {
                throw new ClientException(e);
            }
        }
}
复制代码  上面这段代码就是通过获取到的负载进行执行请求,但是这个时候 服务还没有选择,我们跟进去 submit 请求看一看究竟:
public Observable<T> submit(final ServerOperation<T> operation) {
        final ExecutionInfoContext context = new ExecutionInfoContext();
        // .........
        Observable<T> o = 
                (server == null ? selectServer() : Observable.just(server))
                .concatMap(new Func1<Server, Observable<T>>() {
                    //........
                });
       // .......
}
复制代码  可以看到这里有个 selectServer的方法 ,跟进去:
public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
        String host = null;
        int port = -1;
        if (original != null) {
            host = original.getHost();
        }
        if (original != null) {
            Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original);        
            port = schemeAndPort.second();
        }
    // Various Supported Cases
    // The loadbalancer to use and the instances it has is based on how it was registered
    // In each of these cases, the client might come in using Full Url or Partial URL
    ILoadBalancer lb = getLoadBalancer();
    if (host == null) {
        // ............
    } else {
        // ...........if (shouldInterpretAsVip) {
            Server svc = lb.chooseServer(loadBalancerKey);
            if (svc != null){
                host = svc.getHost();
                if (host == null){
                    throw new ClientException(ClientException.ErrorType.GENERAL,
                            "Invalid Server for :" + svc);
                }
                logger.debug("using LB returned Server: {} for request: {}", svc, original);
                return svc;
            } else {
                // just fall back as real DNS
                logger.debug("{}:{} assumed to be a valid VIP address or exists in the DNS", host, port);
            }
        } else {
            // consult LB to obtain vipAddress backed instance given full URL
            //Full URL execute request - where url!=vipAddress
            logger.debug("Using full URL passed in by caller (not using load balancer): {}", original);
        }
    }
    // ..........
    return new Server(host, port);
}
复制代码  可以看到的是这里获取到了之前构造好的 ZoneAwareLoadBalancer 然后调用 chooseServer 方法获取server ,这个是跟Ribbon 中是一样的流程,这里就不赘述了。
  获取到了server 后,会回调先前 executeWithLoadBalancer 方法里构造的 ServerOperation 的 call 方法:
return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
复制代码  然后会执行 AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig) 进行最后的调用,实际上这里走的是 FeignLoadBalancer#execute
@Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
            throws IOException {
        Request.Options options;
        if (configOverride != null) {
            RibbonProperties override = RibbonProperties.from(configOverride);
            options = new Request.Options(override.connectTimeout(this.connectTimeout),
                    override.readTimeout(this.readTimeout));
        }
        else {
            options = new Request.Options(this.connectTimeout, this.readTimeout);
        }
        Response response = request.client().execute(request.toRequest(), options);
        return new RibbonResponse(request.getUri(), response);
}
复制代码  而这里调用的 request.client().execute(request.toRequest(), options) 则是 DefaultFeignLoadBalancedConfiguration 注入的 LoadBalancerFeignClient ,在构造 LoadBalancerFeignClient 的时候 ,传递了个 feign.Client.Default ,然后利用 feign.Client.Default 构造了一个 RibbonRequest。
  所以这里走 feign.Client.Default#execute :
@Override
public Response execute(Request request, Options options) throws IOException {
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
}
复制代码  利用 JDK 提供的 HttpURLConnection 发起远程的 HTTP通讯。至此发起请求的流程就完成了。下面附上一张这个过程的流程图,对于Ribbon的调用过程请参考 :Ribbon 源码分析。
4. 总结
Spring Cloud OpenFeign 的核心工作原理经上文探究可以非常简单的总结为:
- 通过 @EnableFeignCleints触发 Spring 应用程序对classpath中@FeignClient修饰类的扫描
- 解析到 @FeignClient修饰类后,Feign框架通过扩展Spring Bean Deifinition的注册逻辑, 最终注册一个FeignClientFacotoryBean进入 Spring 容器
- Spring 容器在初始化其他用到 @FeignClient接口的类时, 获得的是FeignClientFacotryBean产生的一个代理对象Proxy.
- 基于 java 原生的动态代理机制, 针对 Proxy 的调用, 都会被统一转发给 Feign 框架所定义的一个InvocationHandler, 由该Handler完成后续的HTTP 转换, 发送, 接收, 翻译HTTP响应的工作。 



















