一、核心组件与概念
关于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 方法中做了两件事:
- 注册 @EnableFeignClients 提供的自定义配置类中的相关 Bean 实例
- 根据 @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);
}