OpenFeign 源码解析

一、核心组件与概念

关于OpenFeign源码阅读,可从两个方面来进行。首先是被@FeignClient注解修饰的接口类(FeignClient 接口类)是如何创建的,就是其Bean实例是如何被创建的。然后是调用FeignClient 接口类网络请求相关的函数时,OpenFeign 时如何发送网络请求的。对于OpenFeign 相关的类,也可以以此角度来看,一部分是初始化相应的Bean 实例,另一部分是用来在调用方法时的网络请求。

二、动态注册 BeanDefinition

OpenFeign可以通过多种方式进行自定义配置,这些配置的不同会导致结仇初始化的时候生成不同的实例Bean。从而控制 OpenFeign的相关行为,譬如请求的网络编码、压缩和日志处理等。所以只有了解这些,对我们使用 OpenFeign有着很大的作用,Spring Cloud所有项目的配置和实例初始化的过程和原理其实是相通的,因此了解OpenFeign,便可触类旁通。

1、FeignClientRegistrar

这里首先要介绍一下 @EnableFeignClients 的基本作用,@EnableFeignClients 其实就像是一个开关,OpenFeign所有的操作都是从它开始的。@EnableFeignClients有三个作用,分别是:引入FeignClientsRegistrar、指定扫描包的信息(指定FeignClient接口类所在的包名)、指定FeignClient接口类的自定义配置类,@EnableFeignClients 注解代码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

    // 指定需要的扫描包,下面的两个函数也是如此
   String[] value() default {};

   String[] basePackages() default {};

   Class<?>[] basePackageClasses() default {};
   // 指定自定义 feign client 的自定义配置,配置 Decoder、Encoder、Contract等组件
   Class<?>[] defaultConfiguration() default {};
   // 指定被 @FeignClients 修饰的类,如果不为空,那么路径自动检测则会被关闭
   Class<?>[] clients() default {};

}

上面的代码中,可以看到通过 @Import 注解引入了 FeignClientsRegistrar ,而它又是 ImportBeanDefinitionRegistrar 的子类,在Spring 里是通过 ImportBeanDefinitionRegistrar 来动态注册 BeanDefinition的。OpenFeign 是通过 FeignClientsRegistrar 来处理被 @FeignClient 修饰的接口类的,将这些接口类的注册到 Spring 容器中,这样也就可以通过 @Autowired 等方式来自动装在这些接口类的 Bean 实例。FeignClientsRegistrar 部分代码如下:

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    // 省略部分代码
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
       // 从 EnableFeignClients 的属性来构建 Feign 的自定义 Configuration 进行注册
       registerDefaultConfiguration(metadata, registry);
       // 从扫描 package,注册被 @FeignClient 修饰的接口类
       registerFeignClients(metadata, registry);
    }
    // 省略部分代码
}

在 FeignClientsRegistrar#registerBeanDefinitions 方法中做了两件事:

  1. 注册 @EnableFeignClients 提供的自定义配置类中的相关 Bean 实例
  2. 根据 @EnableFeignClients 扫描包的信息扫描器路径下被 @FeignClient 修饰的接口类,然后将其注册成 Bean 实例

@EnableFeignClients 注解的自定义配置类是被 @Configuration 修饰的,它会提供一系列组装 FeignClient 的各类组件实例。譬如:Client、Targeter、Decoder、Encoder和Contract等。那这里就看看 FeignClientsRegistrar#registerDefaultConfiguration 方法具体实现。代码如下:

private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   // 获取到 metadata 中关于 @EnableFeignClients 注解属性的键值对
   Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

   // 如果 EnableFeignClients 配置了 defaultConfiguration,那么才进行下一步操作,否则,便会使用默认的 FeignConfiguration
   if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
      String name;
      if (metadata.hasEnclosingClass()) {
         name = "default." + metadata.getEnclosingClassName();
      }
      else {
         name = "default." + metadata.getClassName();
      }
      registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
   }
}

上面的代码逻辑很简单,就是根据 @EnableFeignClients 注解是否设置了defaultConfiguration,如果设置了,才调用 FeignClientsRegistrar#registerClientConfiguration 方法进行 BeanDefinitionRegistry 的注册,FeignClientsRegistrar#registerClientConfiguration 方法代码如下:

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
   // 使用 BeanDefinitionBuilder 生成 BeanDefinition,然后通过 BeanDefinitionRegistry 进行注册
   BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
   builder.addConstructorArgValue(name);
   builder.addConstructorArgValue(configuration);
   registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
         builder.getBeanDefinition());
}

BeanDefinitionRegistry 是Spring Framework 中的注册器(接口),调用其 BeanDefinitionRegistry#registerBeanDefinition 方法,便是将 BeanDefinition 注册到 Spring 容器中,而其 name 也就是 BeanDefinition 的实例名称。

从 BeanDefinitionRegistry#registerBeanDefinition 方法参数中,可以看到其 beanName 参数是通过 name 和 FeignClientSpecification.class.getSimpleName()进行拼接而成的,这里需要关注一下 FeignClientSpecification 这个类,它实现了 NamedContextFactory.Specification 接口,同时它也是 OpenFeign 中的一个重要的环节,因为它持有自定义配置类所提供的组件实例,以供 OpenFeign 使用。在 Spring Cloud 中乃是通过 NamedContextFactory 来创建一系列的运行上下文,来让对应的 Specification 能在这些上下文中创建实例对象。这样可以使的各个子上下文中的实例对象是相互独立的,互相不影响,也可以方便的通过子上下文管理一系列不同的实例对象。这里的 NamedContextFactory 类具有三个功能:

  • 创建 AnnotationConfigApplicationContext 子上下文
  • 在子上下文中创建并获取Bean实例
  • 当子上下文销毁时,清除其中的Bean实例

而在 OpenFeign 中继承 NamedContextFactory ,则是用于存储 OpenFeign 的各类组件实例。

FeignAutoConfiguration 是 OpenFeign 的自动配置类,它提供了 FeignContext 、CloseableHttpClient、OkHttpClient 等实例。这里暂只关注 FeignContext ,在构建 FeignContext 实例的时候,会将之前注册的 FeignClientSpecification 通过 NamedContextFactory#setConfigurations 设置给 FeignContext 实例。这里处理了 默认配置类 FeignClientsConfiguration 和自定义配置类替换的问题,如果 FeignClientsRegistrar 没有注册自定义配置类,那么 configurations 将不包含 FeignClientSpecification 对象,否则在会在 setConfigurations 方法进行默认配置类的替换。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,
      FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
    
   // 省略部分代码
   
   @Autowired(required = false)
   private List<FeignClientSpecification> configurations = new ArrayList<>();

   // 省略部分代码

   @Bean
   public FeignContext feignContext() {
      FeignContext context = new FeignContext();
      context.setConfigurations(this.configurations);
      return context;
   }
   // 省略部分代码
}


public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
   // 将默认的 FeignClientSpecification 作为参数传给构造函数
   public FeignContext() {
      super(FeignClientsConfiguration.class, "feign", "feign.client.name");
   }
   // 省略部分代码
}

从上面代码可见 NamedContextFactory 是 FeignContext 的父类,其 NamedContextFactory#createContext 方法会创建具有名称的AnnotationConfigApplicationContext 实例作为当前上下文的子上下文。这些 AnnotationConfigApplicationContext 实例可以管理 OpenFeign组件的不同实例。NamedContextFactory#createContext 方法代码如下:

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
      implements DisposableBean, ApplicationContextAware {
  // 省略部分代码
  protected AnnotationConfigApplicationContext createContext(String name) {
       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
       // 获取该上下文所对应的 configuration, 如果有的话,就注册到子 context 中
       if (this.configurations.containsKey(name)) {
          for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
             context.register(configuration);
          }
       }
       // 注册 default configuration,也就是 FeignClientsRegistrar 类的 FeignClientsRegistrar#registerDefaultConfiguration
       // 方法中注册的 Configuration
       for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
          if (entry.getKey().startsWith("default.")) {
             for (Class<?> configuration : entry.getValue().getConfiguration()) {
                context.register(configuration);
             }
          }
       }
       // 注册 PropertyPlaceholderAutoConfiguration 和 FeignClientsConfiguration 配置类
       context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
       // 设置子上下文的 Environment 的 PropertySource 属性源
       context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
             Collections.<String, Object>singletonMap(this.propertyName, name)));
       // 所有 context 的 parent 都相同,这样一些相同的 bean 可以通过 parent context 来获取
       if (this.parent != null) {
          // Uses Environment from parent as well as beans
          context.setParent(this.parent);
          // jdk11 issue
          // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
          context.setClassLoader(this.parent.getClassLoader());
       }
       context.setDisplayName(generateDisplayName(name));
       context.refresh();
       return context;
    }
    // 省略部分代码
}

上面代码可以看出 NamedContextFactory 创建的 AnnotationConfigApplicationContext实例的 name 是唯一的,其每一个 AnnotationConfigApplicationContext 实例都会注册部分配置类,便可以给出一系列基于配置类生成的组件实例,那么可以根据 name 来管理这一系列组件实例,来为不同的 FeignClient 准备不同配置组件的实例,例如:Decoder、Encoder 等。

2、扫描类信息

FeignClientsRegistrar 类所做的第二件事情,便是扫描指定包下的类文件,注册 @FeignClient 注解修饰的接口类,FeignClientsRegistrar#registerFeignClients 代码如下:

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

   LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
   // 获取 EnableFeignClients 所有属性的集合
   Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
   final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
   if (clients == null || clients.length == 0) {
      // 生成自定义的 ClassPathScanningProvider
      ClassPathScanningCandidateComponentProvider scanner = getScanner();
      scanner.setResourceLoader(this.resourceLoader);
      // 根据 Annotation 来进行类型过滤,只会扫描被 @FeignClient 注解修饰的接口类
      scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
      // 获取所有的 Packages
      Set<String> basePackages = getBasePackages(metadata);
      // 遍历上述所获取的 basePackages
      for (String basePackage : basePackages) {
         candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
      }
   }
   else {
      for (Class<?> clazz : clients) {
         candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
      }
   }

   // 遍历所有的 candidateComponents
   for (BeanDefinition candidateComponent : candidateComponents) {
      if (candidateComponent instanceof AnnotatedBeanDefinition) {
         // verify annotated class is an interface
         AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
         AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
         Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");

         // 从这些 BeanDefinition 中获取 FeignClient 的属性值
         Map<String, Object> attributes = annotationMetadata
               .getAnnotationAttributes(FeignClient.class.getCanonicalName());

         String name = getClientName(attributes);
         // 对某个单独的 FeignClient 的 configuration 进行配置
         registerClientConfiguration(registry, name, attributes.get("configuration"));

         // 注册 FeignClient 的 BeanDefinition
         registerFeignClient(registry, annotationMetadata, attributes);
      }
   }
}

FeignClientsRegistrar#registerFeignClients 中依据 @EnableFeignClients 注解的属性来获取扫描包路径信息,然后再根据包路径来获取其路径下被 @FeignClient 注解修饰的接口类的 BeanDefinition,最后调用 FeignClientsRegistrar#registerFeignClient 方法来动态注册 BeanDefinition。

在 FeignClientsRegistrar#registerFeignClient 方法中有一些细节是值得注意的,这些细节有利于加深对 Spring Framework 框架的学习。首先是如何自定义 Spring 类扫描器,即如何使用 ClassPathScanningCandidateComponentProvider 和 各类 TypeFilter。在这里使用 AnnotationTypeFilter,来过滤出那些被 @FeignClient 注解修饰的接口类,这里先看看 FeignClientsRegistrar#getScanner 方法,代码如下:

protected ClassPathScanningCandidateComponentProvider getScanner() {
   return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
      @Override
      protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
         boolean isCandidate = false;
         // 判断 beanDefinition 是否是内部类,否则直接返回 false
         if (beanDefinition.getMetadata().isIndependent()) {
             // 判断是否为接口类,所有实现的接口是有一个,并且该接口是 Annotation,否则直接返回false
            if (!beanDefinition.getMetadata().isAnnotation()) {
               isCandidate = true;
            }
         }
         return isCandidate;
      }
   };
}

然后再看看 FeignClientsRegistrar#registerFeignClient 方法,在这个方法里,创建 FeignClientFactoryBean 工厂,再通过其来进行实例化的操作, FeignClientsRegistrar#registerFeignClient 方法代码如下:

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
      Map<String, Object> attributes) {
   String className = annotationMetadata.getClassName();
   Class clazz = ClassUtils.resolveClassName(className, null);
   ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
         ? (ConfigurableBeanFactory) registry : null;
   String contextId = getContextId(beanFactory, attributes);
   String name = getName(attributes);
   // 创建 FeignClientFactoryBean
   FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
   factoryBean.setBeanFactory(beanFactory);
   factoryBean.setName(name);
   factoryBean.setContextId(contextId);
   factoryBean.setType(clazz);
   factoryBean.setRefreshableClient(isClientRefreshEnabled());
   BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
      factoryBean.setUrl(getUrl(beanFactory, attributes));
      factoryBean.setPath(getPath(beanFactory, attributes));
      factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
      Object fallback = attributes.get("fallback");
      if (fallback != null) {
         factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
               : ClassUtils.resolveClassName(fallback.toString(), null));
      }
      Object fallbackFactory = attributes.get("fallbackFactory");
      if (fallbackFactory != null) {
         factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
               : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
      }
      // 实例化开始
      return factoryBean.getObject();
   });
   definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
   definition.setLazyInit(true);
   validate(attributes);

   AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
   beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
   beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

   // has a default, won't be null
   boolean primary = (Boolean) attributes.get("primary");

   beanDefinition.setPrimary(primary);

   String[] qualifiers = getQualifiers(attributes);
   if (ObjectUtils.isEmpty(qualifiers)) {
      qualifiers = new String[] { contextId + "FeignClient" };
   }

   BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
   BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    // 注册 BeanDefinition
   registerOptionsBeanDefinition(registry, contextId);
}

三、实例初始化

FeignClientFactoryBean 是工厂类,Spring 容器通过调用它的 FeignClientFactoryBean#getObject 方法来获取对应的 Bean 实例。被 @FeignClient 注解修饰的接口都是通过 FeignClientFactoryBean#getObject 方法进行实例化,代码如下:

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

<T> T getTarget() {
   FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
         : applicationContext.getBean(FeignContext.class);
   Feign.Builder builder = feign(context);

   if (!StringUtils.hasText(url)) {

      if (LOG.isInfoEnabled()) {
         LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
      }
      if (!name.startsWith("http")) {
         url = "http://" + name;
      }
      else {
         url = name;
      }
      url += cleanPath();
      return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
   }
   if (StringUtils.hasText(url) && !url.startsWith("http")) {
      url = "http://" + url;
   }
   String url = this.url + cleanPath();
   // 调用 FeignClient 的 getInstance 方法获取 client 对象
   Client client = getOptional(context, Client.class);
   // 因为有具体 url,所以就不需要负载均衡,所以除去 LoadBalancerFeignClient 实例
   if (client != null) {
      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();
      }
      if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
         // not load balancing because we have a url,
         // but Spring Cloud LoadBalancer is on the classpath, so unwrap
         client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
      }
      builder.client(client);
   }
   Targeter targeter = get(context, Targeter.class);
   return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}

这里继续看 FeignClientFactoryBean#getOptional 方法,在其方法里调用 getInstance 方法,这里乃是从 FeignContext 对应名称的上下文中获取 Client 类型的 Bean 实例。前面已有讲述 FeignContext 的作用,这里就不加赘述了,getInstance 方法代码如下:

public <T> T getInstance(String name, Class<T> type) {
   AnnotationConfigApplicationContext context = getContext(name);
   try {
      // 从对应的 上下文中获取 Bean 实例,如果对应的上下文没有则从父上下文中获取
      return context.getBean(type);
   }
   catch (NoSuchBeanDefinitionException e) {
      // ignore
   }
   return null;
}

在默认情况下,子上下文中并没有这些 Bean实例,所以只能从父上下文中获取,而父上下文中的 Client 类型的 BeanDefinition 是在 FeignAutoConfiguration 中进行注册的,但当子上下文注册的配置类提供 Client 实例时,子上下文会直接将自己的配置类的 Client 实例进行返回,否则都是由父上下文返回默认实例,Client 在 FeignAutoConfiguration 中代码如下:

@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
   return new ApacheHttpClient(httpClient);
}

前面提到的 FeignClientFactoryBean#getTarget 方法中最后的 Targeter 是一个接口,它内部的 Targeter#target 方法可以生成对应的实例对象,在这里它由两个实现类,分别是:DefaultTargeter 和 FeignCircuitBreakerTargeter,DefaultTargeter#target 实现如下:

class DefaultTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
			Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}

}

这里可以看到 DefaultTargeter#target 方法中所传的一个参数是 Feign.Builder,Feign.Builder是由 FeignClientFactoryBean#feign 方法创建的,在这个方法里还会设置 FeignLoggerFactory、Encoder、Decoder、Contract等组件,而这些组件的实际是通过 FeignContext 获取的,也就是说这些实例也是可配置的,可以通过 OpenFeign 的配置机制来为不同的 FeignContext 配置不同的组件实例,FeignClientFactoryBean#feign 实现如下:

protected Feign.Builder feign(FeignContext context) {
   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));
   // @formatter:on

   configureFeign(context, builder);
   applyBuildCustomizers(context, builder);

   return builder;
}

Feign.Builder 负责生成那些被 @FeignClient 修饰的接口类实例,它通过 Java 的反射机制,构造 InvocationHandler 实例并注册到 FeignClient 上,当 FeignClient 方法被调用的时候,InvocationHandler 的回调函数会被调用,OpenFeign 会在其回调函数中发送网络请求,feign.Feign.Builder#build 方法实现如下:

  public Feign build() {
    Client client = Capability.enrich(this.client, capabilities);
    Retryer retryer = Capability.enrich(this.retryer, capabilities);
    List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
        .map(ri -> Capability.enrich(ri, capabilities))
        .collect(Collectors.toList());
    Logger logger = Capability.enrich(this.logger, capabilities);
    Contract contract = Capability.enrich(this.contract, capabilities);
    Options options = Capability.enrich(this.options, capabilities);
    Encoder encoder = Capability.enrich(this.encoder, capabilities);
    Decoder decoder = Capability.enrich(this.decoder, capabilities);
    InvocationHandlerFactory invocationHandlerFactory =
        Capability.enrich(this.invocationHandlerFactory, capabilities);
    QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
        new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
            logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
    ParseHandlersByName handlersByName =
        new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
            errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  }
}

其实 feign.ReflectiveFeign#newInstance 方法才是生成 FeignClient 实例的关键实现,它里面做了两件事情,一是:扫描 FeignClient 接口类的所有函数,来生成对应的 Handler,二是使用 Proxy 生成 FeignClient 的实例对象,feign.ReflectiveFeign#newInstance 方法实现如下:

// feign.Feign.Builder#target(feign.Target<T>)
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

@Override
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
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      defaultMethodHandlers.add(handler);
      methodToHandler.put(method, handler);
    } else {
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  // 通过 InvocationHandlerFactory 创建 InvocationHandler
  InvocationHandler handler = factory.create(target, methodToHandler);
  T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
      new Class<?>[] {target.type()}, handler);

  // 将 defaultMethodHandlers 绑定到 proxy 中
  for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    defaultMethodHandler.bindTo(proxy);
  }
  return proxy;
}

1、扫描函数信息

在扫描 FeignClient 接口类所有生成对应 Handler 的时候,OpenFeign 对调用该函数发送网络请求的模板,也就是 RequestTemplate 实例。RequestTemplate 中包含了发送网络请求时的 URL 和函数填充的信息。@RequestMapping、@PathVariable 等注解信息也会包含在 RequestTemplate 中的,用于函数填充。ParseHandlersByName#apply 方法其实就是这一过程的具体实现。ParseHandlersByName#apply 方法里首先会使用 Contract 来解析接口类中的函数信息,并检查函数的合法性,然后根据函数的不同类型为每个函数生成一个 BuildEncodedTemplateFromArgs 对象,最后再使用 SynchronousMethodHandler.Factory 来创建 MethodHandler 实例,ParseHandlersByName#apply 实现如下:

  public Map<String, MethodHandler> apply(Target target) {
    // 获取 type 的所有方法信息,会根据注解生成每个方法的 RequestTemplate 
    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;
  }
}

OpenFeign中默认的 Contract 的实现是 SpringMvcContract,SpringMvcContract 的父类又是 BaseContract,这样 BaseContract 也就是 Contract 的子类中的众多一员。Contract#parseAndValidateMetadata 方法会解析与 HTTP请求相关的所有函数基本信息和注解信息,代码如下:

// org.springframework.cloud.openfeign.support.SpringMvcContract#parseAndValidateMetadata
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
   processedMethods.put(Feign.configKey(targetType, method), method);
   // 调用父类的 BaseContract#parseAndValidateMetadata 函数
   MethodMetadata md = super.parseAndValidateMetadata(targetType, method);

   RequestMapping classAnnotation = findMergedAnnotation(targetType, RequestMapping.class);
   // 处理 RequestMapping 注解
  if (classAnnotation != null) {
      // produces - use from class annotation only if method has not specified this
      if (!md.template().headers().containsKey(ACCEPT)) {
         parseProduces(md, method, classAnnotation);
      }

      // consumes -- use from class annotation only if method has not specified this
      if (!md.template().headers().containsKey(CONTENT_TYPE)) {
         parseConsumes(md, method, classAnnotation);
      }

      // headers -- class annotation is inherited to methods, always write these if
      // present
      parseHeaders(md, method, classAnnotation);
   }
   return md;
}

在 BaseContract#parseAndValidateMetadata(java.lang.Class<?>, java.lang.reflect.Method) 方法中会依次解析接口类的注解,函数注解和函数的参数注解,将这些注解包含的信息封装到 MethodMetadata 对象中,然后将其返回,BaseContract#parseAndValidateMetadata(java.lang.Class<?>, java.lang.reflect.Method) 方法实现如下:

protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
  final MethodMetadata data = new MethodMetadata();
  data.targetType(targetType);
  data.method(method);
  // 函数的返回值
  data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
  // 函数 Feign 相关的唯一配置键
  data.configKey(Feign.configKey(targetType, method));

  // 获取并处理 class 的注解信息
  if (targetType.getInterfaces().length == 1) {
    processAnnotationOnClass(data, targetType.getInterfaces()[0]);
  }
  // 调用子类 processAnnotationOnClass 的实现
  processAnnotationOnClass(data, targetType);

  // 处理修饰 method 的注解信息
  for (final Annotation methodAnnotation : method.getAnnotations()) {
    processAnnotationOnMethod(data, methodAnnotation, method);
  }
  if (data.isIgnored()) {
    return data;
  }
  checkState(data.template().method() != null,
      "Method %s not annotated with HTTP method type (ex. GET, POST)%s",
      data.configKey(), data.warnings());
  // 函数参数类型
  final Class<?>[] parameterTypes = method.getParameterTypes();
  final Type[] genericParameterTypes = method.getGenericParameterTypes();

  // 函数参数的注解类型
  final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
  final int count = parameterAnnotations.length;
  // 一次处理各个函数参数注解
  for (int i = 0; i < count; i++) {
    boolean isHttpAnnotation = false;
    if (parameterAnnotations[i] != null) {
      // 处理参数的注解,并且返回该参数来指明是否将要发送请求的 body,除body之外,可能是path、param等
      isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
    }

    if (isHttpAnnotation) {
      data.ignoreParamater(i);
    }

    if (parameterTypes[i] == URI.class) {
      data.urlIndex(i);
    } else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class) {
      if (data.isAlreadyProcessed(i)) {
        checkState(data.formParams().isEmpty() || data.bodyIndex() == null,
            "Body parameters cannot be used with form parameters.%s", data.warnings());
      } else {
        checkState(data.formParams().isEmpty(),
            "Body parameters cannot be used with form parameters.%s", data.warnings());
        checkState(data.bodyIndex() == null,
            "Method has too many Body parameters: %s%s", method, data.warnings());
        // 表明发送请求body等参数未知和参数类型
        data.bodyIndex(i);
        data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
      }
    }
  }

  // 检验
  if (data.headerMapIndex() != null) {
    checkMapString("HeaderMap", parameterTypes[data.headerMapIndex()],
        genericParameterTypes[data.headerMapIndex()]);
  }

  if (data.queryMapIndex() != null) {
    if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) {
      checkMapKeys("QueryMap", genericParameterTypes[data.queryMapIndex()]);
    }
  }

  return data;
}

SpringMvcContract#processAnnotationOnClass 方法的作用是处理接口类注解,该函数在 当前方法中可能会被调用两次,如果 targatType 只继承或者实现一种接口时,先处理该接口的注解,再处理targetType的注解,否则只会处理 targetType 的注解。@RequestMapping 在修饰 FeignClient 接口类时,其value 所代表的值会被记录下类,它是该 FeignClient 下所有请求 URL 的前置路径,SpringMvcContract#processAnnotationOnClass 方法的代码如下:

@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
   if (clz.getInterfaces().length == 0) {
      // 获取 @RequestMapping 注解信息,并设置 MethodMetadata.template的数据
      RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
      if (classAnnotation != null) {
         // Prepend path from class annotation if specified
         if (classAnnotation.value().length > 0) {
            String pathValue = emptyToNull(classAnnotation.value()[0]);
            pathValue = resolve(pathValue);
            if (!pathValue.startsWith("/")) {
               pathValue = "/" + pathValue;
            }
            // 处理 @RequestMapping 的value,一般都是发送请求的 path
            data.template().uri(pathValue);
            if (data.template().decodeSlash() != decodeSlash) {
               data.template().decodeSlash(decodeSlash);
            }
         }
      }
   }
}

SpringMvcContract#processAnnotationOnMethod 方法的主要作用是处理修饰函数的注解。它首先会校验该函数是否被 @RequestMapping 注解修饰,如果没有就直接返回。然后获取该函数所对应的 HTTP 请求的方式,默认是 GET。接着就是处理@RequestMapping 注解中 vlue 属性,解析value中的pathValue,比如value值为/serch/{userId},那么pathValue 的值就是userId。最后处理消费和生产相关的信息,记录媒体类型, SpringMvcContract#processAnnotationOnMethod 方法的实现如下:

@Override
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
   if (CollectionFormat.class.isInstance(methodAnnotation)) {
      CollectionFormat collectionFormat = findMergedAnnotation(method, CollectionFormat.class);
      data.template().collectionFormat(collectionFormat.value());
   }

   if (!RequestMapping.class.isInstance(methodAnnotation)
         && !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
      return;
   }

   RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
   // HTTP Method
   // 处理 HTTP Method
   RequestMethod[] methods = methodMapping.method();
   // 默认 method 是GET
   if (methods.length == 0) {
      methods = new RequestMethod[] { RequestMethod.GET };
   }
   checkOne(method, methods, "method");
   data.template().method(Request.HttpMethod.valueOf(methods[0].name()));

   // path
   // 处理请求路径
   checkAtMostOne(method, methodMapping.value(), "value");
   if (methodMapping.value().length > 0) {
      String pathValue = emptyToNull(methodMapping.value()[0]);
      if (pathValue != null) {
         pathValue = resolve(pathValue);
         // Append path from @RequestMapping if value is present on method
         if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
            pathValue = "/" + pathValue;
         }
         data.template().uri(pathValue, true);
         if (data.template().decodeSlash() != decodeSlash) {
            data.template().decodeSlash(decodeSlash);
         }
      }
   }

   // produces
   // 处理生产
   parseProduces(data, method, methodMapping);

   // consumes
   // 处理消费
   parseConsumes(data, method, methodMapping);

   // headers
   // 处理消息头
   parseHeaders(data, method, methodMapping);

   data.indexToExpander(new LinkedHashMap<>());
}

SpringMvcContract#processAnnotationsOnParameter 方法的作用是处理修饰函数参数的注解信息。这里会根据注解类型来调用不同的 AnnotatedParameterProcessor 的实现类,来解析注解的属性信息,函数参数的注解类型有:@RequestParam、@RequestHeader和@PathVariable。SpringMvcContract#processAnnotationsOnParameter 方法的的具体实现如下:

@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
   boolean isHttpAnnotation = false;

   AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data,
         paramIndex);
   Method method = processedMethods.get(data.configKey());
   // 遍历所有参数注解
   for (Annotation parameterAnnotation : annotations) {
      // 不同的注解类型有不同的 Processor
      AnnotatedParameterProcessor processor = annotatedArgumentProcessors
            .get(parameterAnnotation.annotationType());
      if (processor != null) {
         Annotation processParameterAnnotation;
         // synthesize, handling @AliasFor, while falling back to parameter name on
         // missing String #value():
         // 如果没有缓存的  Processor,则生成一个
         processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation,
               method, paramIndex);
         isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
      }
   }

   // 对参数类型进行处理
   if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
      TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
      if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
         Param.Expander expander = convertingExpanderFactory.getExpander(typeDescriptor);
         if (expander != null) {
            data.indexToExpander().put(paramIndex, expander);
         }
      }
   }
   return isHttpAnnotation;
}

既然说会根据注解类型来调用不同的 AnnotatedParameterProcessor 的实现类,那么 AnnotatedParameterProcessor 便是一个接口,这里它有如下几个主要实现类:PathVariableParameterProcessor、RequestHeaderParameterProcessor、RequestParamParameterProcessor;这几个实现类分别处理如下几个注解:@RequestParam、@RequestHeader和@PathVariable;PathVariableParameterProcessor#processArgument 这个方法时用于处理 @PathVariable 注解修饰的参数的,这里先看一下 PathVariableParameterProcessor#processArgument 方法的具体实现:

@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
   // ANNOTATION 就是 @PathVariable,所以就获取它的值,也就是 @RequestMapping value 中的值
   String name = ANNOTATION.cast(annotation).value();
   checkState(emptyToNull(name) != null, "PathVariable annotation was empty on param %s.",
         context.getParameterIndex());
   // 将 name 设置为 ParameterName
   context.setParameterName(name);

   MethodMetadata data = context.getMethodMetadata();
   // 当 varName 的 url、quries、headers 中不存在时,将name添加到formParams中,因为无法找到对应的值
   String varName = '{' + name + '}';
   if (!data.template().url().contains(varName) && !searchMapValues(data.template().queries(), varName)
         && !searchMapValues(data.template().headers(), varName)) {
      data.formParams().add(name);
   }
   return true;
}

ParseHandlersByName#apply 方法时通过 Contract#parseAndValidateMetadata 方法来啊获得接口类中的所有方法的元数据,这些信息中包含了每个方法所对应的网络请求信息,譬如请求的path、params、headers、body。接下来 apply方法会为每个方法生成一个 MethodHandler。SynchronousMethodHandler.Factory#create 方法便是作此用处的,代码实现如下:

  public MethodHandler create(Target<?> target,
                              MethodMetadata md,
                              RequestTemplate.Factory buildTemplateFromArgs,
                              Options options,
                              Decoder decoder,
                              ErrorDecoder errorDecoder) {
    return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
        logLevel, md, buildTemplateFromArgs, options, decoder,
        errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
  }
}

2、生成 Proxy 接口类

ReflectiveFeign#newInstance 方法的第二部分就是相应接口类的实例对象,并设置方法处理器,代码如下:

// 生成Java 反射 Handler
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
    new Class<?>[] {target.type()}, handler);

// 将 defaultMethodHandler 绑定到 proxy 上
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
  defaultMethodHandler.bindTo(proxy);
}
return proxy;

在这里使用 newProxyInstance 方法类似创建 FeignClient 接口类的实例,让后将InvocationHandler 绑定到所创建的实例上,创建 InvocationHandler 的代码实现如下:

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
  
  interface MethodHandler {
    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}

从上面的代码汇总可以看到 create 方法时处于 InvocationHandlerFactory 的内部类 Default 类中,而 Default 又实现了 InvocationHandlerFactory,其 create 方法返回 ReflectiveFeign.FeignInvocationHandler 对象实例。

ReflectiveFeign 的内部类 FeignInvocationHandler 是 InvocationHandler 的实现类,其主要作用是将接口类相关函数的调用分配给 MethodHandler,即 SynchronousMethodHandler 来处理。当调用接口类实例函数的时候,会直接调用 FeignInvocationHandler 的 invoke 方法,FeignInvocationHandler#invoke 方法实现如下:

static class FeignInvocationHandler implements InvocationHandler {

  private final Target target;
  private final Map<Method, MethodHandler> dispatch;

  FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
    this.target = checkNotNull(target, "target");
    this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
  }

  @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();
    }
    // 将某个函数的调用教给对应的 MethodHandler 来处理
    return dispatch.get(method).invoke(args);
  }
  // 省略部分代码
}

四、函数调用和网络请求

当配置和实例生成结束之后,就可以直接使用 FeignClient 接口类的实例,调用它的函数来发送网络请求。在调用函数的过程中,因为设置了 MethodHandler,所以最终函数调用会执行 SynchronousMethodHandler#invoke 方法。在这个方法中,OpenFeign 会将函数的实际参数值与之前生成的 RequestTemplate 进行结合,然后发送网路请求。

SynchronousMethodHandler#invoke 方法创建了 RequestTemplate 对象。在创建该对象的时候,使用到了之前收集的函数信息MethodMetadata。遍历 MethodMetadata 中参数相关的 indexToName,然后根据索引从 invoke 的参数数组中获得对应的值,将其填入对应的键值对中,然后一次处理查询和头部相关的参数值。

@Override
public Object invoke(Object[] argv) throws Throwable {
  // 根据函数参数创建 RequestTemplate 实例
  RequestTemplate template = buildTemplateFromArgs.create(argv);
  Options options = findOptions(argv);
  Retryer retryer = this.retryer.clone();
  while (true) {
    try {
      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;
    }
  }
}

invoke 方法中调用 RequestTemplate.Factory#create 方法,代码如下:

@Override
public RequestTemplate create(Object[] argv) {
  RequestTemplate mutable = RequestTemplate.from(metadata.template());
  mutable.feignTarget(target);
  if (metadata.urlIndex() != null) {
    int urlIndex = metadata.urlIndex();
    checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
    mutable.target(String.valueOf(argv[urlIndex]));
  }
  Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
  // 遍历 MethodMetadata 中所有关于参数的索引及其对应名称的配置信息
  for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
    int i = entry.getKey();
    // entry.getKey() 就是参数的索引
    Object value = argv[entry.getKey()];
    if (value != null) { // Null values are skipped.
      // indexToExpander 保存着将各种类型参数的值转换为 string 类型的 Expander 转换器
      if (indexToExpander.containsKey(i)) {
        // 将 value 转换为 string
        value = expandElements(indexToExpander.get(i), value);
      }
      for (String name : entry.getValue()) {
        varBuilder.put(name, value);
      }
    }
  }

  RequestTemplate template = resolve(argv, mutable, varBuilder);
  // 设置 queryMap 参数
  if (metadata.queryMapIndex() != null) {
    // add query map parameters after initial resolve so that they take
    // precedence over any predefined values
    Object value = argv[metadata.queryMapIndex()];
    Map<String, Object> queryMap = toQueryMap(value);
    template = addQueryMapQueryParameters(queryMap, template);
  }

  // 设置 headerMap 参数
  if (metadata.headerMapIndex() != null) {
    template =
        addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
  }

  return template;
}

RequestTemplate.Factory#create 方法中的 BuildTemplateByResolvingArgs#resolve 方法中调用了RequestTemplate#resolve(java.util.Map<java.lang.String,?>) 方法,在这个方法里会处理 URL和请求头信息,最后处理请求的Body信息,代码如下:

public RequestTemplate resolve(Map<String, ?> variables) {

  StringBuilder uri = new StringBuilder();

  /* create a new template form this one, but explicitly */
  RequestTemplate resolved = RequestTemplate.from(this);

  if (this.uriTemplate == null) {
    /* create a new uri template using the default root */
    this.uriTemplate = UriTemplate.create("", !this.decodeSlash, this.charset);
  }

  String expanded = this.uriTemplate.expand(variables);
  if (expanded != null) {
    uri.append(expanded);
  }

  /*
   * for simplicity, combine the queries into the uri and use the resulting uri to seed the
   * resolved template.
   */
  if (!this.queries.isEmpty()) {
    /*
     * since we only want to keep resolved query values, reset any queries on the resolved copy
     */
    resolved.queries(Collections.emptyMap());
    StringBuilder query = new StringBuilder();
    Iterator<QueryTemplate> queryTemplates = this.queries.values().iterator();

    while (queryTemplates.hasNext()) {
      QueryTemplate queryTemplate = queryTemplates.next();
      String queryExpanded = queryTemplate.expand(variables);
      if (Util.isNotBlank(queryExpanded)) {
        query.append(queryExpanded);
        if (queryTemplates.hasNext()) {
          query.append("&");
        }
      }
    }

    String queryString = query.toString();
    if (!queryString.isEmpty()) {
      Matcher queryMatcher = QUERY_STRING_PATTERN.matcher(uri);
      if (queryMatcher.find()) {
        /* the uri already has a query, so any additional queries should be appended */
        uri.append("&");
      } else {
        uri.append("?");
      }
      uri.append(queryString);
    }
  }

  /* add the uri to result */
  // 处理url
  resolved.uri(uri.toString());

  /* headers */
  if (!this.headers.isEmpty()) {
    /*
     * same as the query string, we only want to keep resolved values, so clear the header map on
     * the resolved instance
     */
    resolved.headers(Collections.emptyMap());
    for (HeaderTemplate headerTemplate : this.headers.values()) {
      /* resolve the header */
      // 处理头部信息
      String header = headerTemplate.expand(variables);
      if (!header.isEmpty()) {
        /* split off the header values and add it to the resolved template */
        String headerValues = header.substring(header.indexOf(" ") + 1);
        if (!headerValues.isEmpty()) {
          /* append the header as a new literal as the value has already been expanded. */
          resolved.header(headerTemplate.getName(), Literal.create(headerValues));
        }
      }
    }
  }

  if (this.bodyTemplate != null) {
    // 处理 body
    resolved.body(this.bodyTemplate.expand(variables));
  }

  /* mark the new template resolved */
  resolved.resolved = true;
  return resolved;
}

SynchronousMethodHandler#invoke 方法中最后 SynchronousMethodHandler#executeAndDecode 方法中会根据 RequestTemplate 来生成 Request 对象,然后交给 Client 实例发送网络请求,最后返回对应的函数类型的实例,SynchronousMethodHandler#executeAndDecode 方法如下:

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
  // 根据 RequestTemplate 生成 Request
  Request request = targetRequest(template);

  if (logLevel != Logger.Level.NONE) {
    logger.logRequest(metadata.configKey(), logLevel, request);
  }

  Response response;
  long start = System.nanoTime();
  // Client 发送网络请求,Client 可能为 okHttpClient和apacheClient
  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) {
    if (logLevel != Logger.Level.NONE) {
      logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
    }
    throw errorExecuting(request, e);
  }
  long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);


  if (decoder != null)
    return decoder.decode(response, metadata.returnType());

  CompletableFuture<Object> resultFuture = new CompletableFuture<>();
  // 异步处理 response
  asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
      metadata.returnType(),
      elapsedTime);

  try {
    if (!resultFuture.isDone())
      throw new IllegalStateException("Response handling not done");

    return resultFuture.join();
  } catch (CompletionException e) {
    Throwable cause = e.getCause();
    if (cause != null)
      throw cause;
    throw e;
  }
}

在OpenFeign 中也提供了 RequestInterceptor 机制,这个是由 RequestTemplate 生成 Request 的过程中,会调用所有的 RequestInterceptor 对 RequestTemplate 进行处理, target 是生成网络请求的 Request 接口类,SynchronousMethodHandler#targetRequest 的具体实现如下:

// 按照 RequestTemplate 来创建 Request
Request targetRequest(RequestTemplate template) {
  // 使用请求拦截器为每个请求添加固定的header信息,例如 BasicAuthRequestInterceptor
  for (RequestInterceptor interceptor : requestInterceptors) {
    interceptor.apply(template);
  }
  return target.apply(template);
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值