小许的技术驿站——SpringCloud-OpenFeign源码解析笔记
作者:互联网
小许的技术驿站——SpringCloud-OpenFeign源码解析笔记
小弟有一个开源项目,希望大家可以多多一键三连,谢谢大家
后续的源码解析也都会进行同步更新上去
1、什么是OpenFeign?
根据上篇博客能够看到在使用 Eureka
,loadBalance
来进行服务调用时,都需要创建 RestTemplate
组件进行 Http
请求访问,但是如果每次都是用组件进行请求的话,我们在代码上面会写的很麻烦,而且也会很繁琐,所以 SpringCloud
整合了 Feign
客户端。而 Feign
是一个声明性web服务客户端。Spring Cloud
集成了Eureka
、Spring Cloud CircuitBreaker
和Spring Cloud LoadBalancer
,在使用Feign
时提供一个负载均衡的http
客户端。并且可以解析Spring MVC
注解,意思就是说,我们使用 Feign
那么我们就可以不需要在手动使用 RestTemplate
组件进行请求 Http
接口了。
2、怎么使用OpenFeign?
我们首先改造一下我们消费者的代码。
/**
* 开启Eureka-CLient客户端,OpenFeign客户端
*/
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class EurekaServiceBApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServiceBApplication.class, args);
}
@RestController
@Configuration
public class ServiceBController {
/**
* SayHello 请求OpenFeign组件
**/
@Autowired
SayHelloClient sayHelloClient;
@RequestMapping(value = "/greeting/{name}", method = RequestMethod.GET)
public String greeting(@PathVariable("name") String name) {
return sayHelloClient.test(name);
}
}
}
/**
* 声明一个SayHello OpenFeign请求客户端
*/
@FeignClient("ServiceA")
public interface SayHelloClient {
/**
* 基于Http请求 ServiceA 的 /test/sayHello/{test} 接口
**/
@GetMapping("/test/sayHello/{test}")
String test(@PathVariable("test") String test);
}
根据上面的代码我们在请求 ServiceA/test/sayHello/
接口时就不需要使用 RestTemplate
组件了,而且在我们开发的过程中,就可以直接把Controller
层加上一个接口打包成一个Jar
包进行调用即可,这样能够保证接口的请求地址与参数都不会出问题,也能够简化开发。
3、@EnableFeignClients 注解详解
小知识:在看Spring
相关的代码时,应该多去关注一些注解中 @Import
导入的类,在 SpringBoot
中应该多多关注 xxxAutoConfiguration
类。
我们在 @EnableFeignClients
注解中能够看到导入了 FeignClientsRegistrar.class
类,从Spring
源码结构来看,一般这样的类都是该注解的关键处理类,所以我们应该仔细阅读一下。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册全局默认的FeignClient配置组件
registerDefaultConfiguration(metadata, registry);
// 注册 @FeignClient 标注的接口
registerFeignClients(metadata, registry);
}
}
在 registerDefaultConfiguration(metadata, registry)
主要是根据 @EnableFeignClients
注解中的 defaultConfiguration
属性加载默认的全局配置类,如果没有配置就默认使用 FeignClientSpecification.class
进行注册,而最主要的是如何去注册 被@FeignClient
注解标注的接口。
3.1 registerFeignClients(metadata, registry);
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//被@FeignClient标注可以注册为 SpringBean的集合
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// 获取@EnableFeignClients的全部属性值
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
// 获取@EnableFeignClients属性中的clients属性
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
//如果没有手动配置clients属性那么就通过包路径扫描进行注册
if (clients == null || clients.length == 0) {
//获取根据 classpath路径进行扫描的Provider类
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
//扫描被@FeignClient注解标注的接口
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
//获取扫描包集合
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
// 把@EnableFeignClients的clients属性class数组添加到candidateComponents集合中
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// 循环注册FeignClient SpringBean
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");
// 获取接口上面 @FeignClient标注的注解熟悉
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
//获取 @FeignClient 以contextId->value->name->serviceId 进行逐步获取feign名称
String name = getClientName(attributes);
//注册当前FeignClient的配置类
registerClientConfiguration(registry, name, attributes.get("configuration"));
//注册FeignClient到Spring容器中
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
从上面的源码能够看到,其实 Feign
的模式是和 loadbalance
源码的模式是差不多的,都是一个 Client
或者一个 loadbalance
都会去维护一个 Spring ApplicationContext
,需要什么组件直接通过 getBean()
进行获取,其配置全部都是通过配置类进行注册到 Spring
上下文进行初始化与配置。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
// FeignClient 接口类名称
String className = annotationMetadata.getClassName();
// 反射获取FeignClient 接口类
Class clazz = ClassUtils.resolveClassName(className, null);
// 获取SpringBean Factory
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
//创建 FeignClientBean并且设置一些Bean的一些属性
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);//设置Bean工厂
factoryBean.setName(name);//设置Bean名称
factoryBean.setContextId(contextId);//设置 Feign的上下文Id
factoryBean.setType(clazz);//设置Bean class类型
factoryBean.setRefreshableClient(isClientRefreshEnabled());// 设置是否自动刷新Client客户端,默认False
// 通过Spring BeanDefinitionBuilder 创建Bean
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
// 设置当前Feign客户端的请求Url地址
factoryBean.setUrl(getUrl(beanFactory, attributes));
// 设置当前Feign客户端的请求地址前缀
factoryBean.setPath(getPath(beanFactory, attributes));
// 设置当前Feign客户端请求404时,是解析请求还是抛出异常,默认false
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
//判断当前Feign客户端是否有返回处理对象,如果有就设置,当时返回的处理对象必须是SpringBean
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
//判断当前Feign客户端是否有返回处理对象工厂,该工厂必须要生成返回处理对象,也必须是SpringBean,如果有就设置
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
// 这里是最关键的一步,获取到的对象被 FeignClientFactoryBean动态代理处理过,在调用时会自动去请求Eureka服务接口
return factoryBean.getObject();
});
// 自动注入方式,根据类型自动注入
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// 是否是懒加载
definition.setLazyInit(true);
validate(attributes);
//从BeanDefinitionBuilder中获取上面配置的 BeanDefinition
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
// 设置当前的 BeanDefinition 注入类型是 接口类型
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
// 设置 feignClientsRegistrarFactoryBean 其根本是 FeignClientFactoryBean 对象,
// 主要是 getObject()方法获取被动态代理过的 FeignClient接口
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// 当前的beanDefinition是首先的,首先注入的
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
// 当前Feign是否有别名,默认为空
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
//创建一个带有名称与别名的 Holder,其本身还是beanDefinition
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
// 注册 beanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
// 注册一个 refreshScope 默认不创建
registerOptionsBeanDefinition(registry, contextId);
}
根据这段源码我们看到了,其实被 @FeignClient
标注的接口,会被注册成 SpringBean
而在通过 FeignClientFactoryBean
进行获取 Bean
的时候,获取到的 Bean
对象其实是被 Feign
动态代理生成过了,这时候在调用接口的时候,就会被 Feign
通过动态代理的方式去请求。
3.2 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 并且加载对应的 Feign配置文件参数
Feign.Builder builder = feign(context);
//判断在 @FeignClient注解中是否配置了 url 如果没有配置那么就默认使用 @FeignClient name 名称作为请求地址
if (!StringUtils.hasText(url)) {
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
// 生成一个 loadbalance的请求动态代理对象
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
// 如果设置了 url属性,那么就根据 url设置请求地址
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
// 获取Eureka Client 客户端,并且判断Eureka Client是使用那种方式进行请求,默认是 FeignBlockingLoadBalancerClient.class
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
// 获取动态代理增强对象,默认是 DefaultTargeter对象
Targeter targeter = get(context, Targeter.class);
// 使用 DefaultTargeter 生成一个动态代理对象
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
读取配置文件配置FeignClient客户端
protected Feign.Builder feign(FeignContext context) {
// 从 Spring 容器中获取 logger工厂,默认是 Slf4jLogger
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(type);
// 从 Spring 容器中获取 Feign.Builder ,并且配置参数初始值
Feign.Builder builder = get(context, Feign.Builder.class)
.logger(logger)//配置日志对象
.encoder(get(context, Encoder.class))//配置请求参数编码器
.decoder(get(context, Decoder.class))//配置请求参数解码器
.contract(get(context, Contract.class));//配置SpringMVC 注解解析器
// 根据配置文件配置FeignClient配置参数
configureFeign(context, builder);
// 执行用户自己定义的创建 Feign Client构建器
applyBuildCustomizers(context, builder);
return builder;
}
private void applyBuildCustomizers(FeignContext context, Feign.Builder builder) {
// 由此能够知道自己定义的Feign构建,必须也是SpringBean
Map<String, FeignBuilderCustomizer> customizerMap = context.getInstances(contextId,
FeignBuilderCustomizer.class);
if (customizerMap != null) {
customizerMap.values().stream().sorted(AnnotationAwareOrderComparator.INSTANCE)
.forEach(feignBuilderCustomizer -> feignBuilderCustomizer.customize(builder));
}
additionalCustomizers.forEach(customizer -> customizer.customize(builder));
}
protected void configureFeign(FeignContext context, Feign.Builder builder) {
//从Spring容器中获取到 Feign Client 配置文件,主要是在 application.properties文件中配置的参数
FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
: applicationContext.getBean(FeignClientProperties.class);
//从Spring容器中获取到当前Feign Client的全局配置
FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
// 配置文件的读取流程是 全局的配置->Default默认配置参数->clientProperties单个feign client配置参数
// 覆盖顺序是对底层的覆盖最上层的
if (properties != null && inheritParentContext) {
if (properties.isDefaultToProperties()) {
//配置FeignClient全局配置参数
configureUsingConfiguration(context, builder);
//配置FeignClient默认配置参数
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
//针对FeignClient单个服务读取配置参数
configureUsingProperties(properties.getConfig().get(contextId), builder);
}
else {
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
configureUsingProperties(properties.getConfig().get(contextId), builder);
configureUsingConfiguration(context, builder);
}
}
else {
configureUsingConfiguration(context, builder);
}
}
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
// 整体配置全部都是从Spring容器中获取对应的Bean对象,然后放入 Feign.Builder 对象中
Logger.Level level = getInheritedAwareOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
}
Retryer retryer = getInheritedAwareOptional(context, Retryer.class);
if (retryer != null) {
builder.retryer(retryer);
}
。。。
}
protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config,
Feign.Builder builder) {
// 通过用户在application.properties配置文件中的配置,进行逐步设置相关参数
if (config == null) {
return;
}
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
//是否自动刷新feign client
if (!refreshableClient) {
connectTimeoutMillis = config.getConnectTimeout() != null ? config.getConnectTimeout()
: connectTimeoutMillis;
readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout() : readTimeoutMillis;
followRedirects = config.isFollowRedirects() != null ? config.isFollowRedirects() : followRedirects;
builder.options(new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS, readTimeoutMillis,
TimeUnit.MILLISECONDS, followRedirects));
}
if (config.getRetryer() != null) {
Retryer retryer = getOrInstantiate(config.getRetryer());
builder.retryer(retryer);
}
。。。
}
获取EurekaClient,并且生成动态代理对象
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
// 从Spring容器中获取到EurekaClient,默认是 FeignBlockingLoadBalancerClient 对象
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// 从Spring容器中获取 Targeter 动态代理类,默认是 DefaultTargeter
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
}
class DefaultTargeter implements Targeter {
// 通过该代码能够看到,动态代理生成的对象还是通过Feign.Builder进行生成的
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
public Feign build() {
// 对当前配置的FeignClient的参数根据this.capabilities集合进行增强,默认 this.capabilities 为空
Client client = (Client)Capability.enrich(this.client, this.capabilities);
Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {
return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
}).collect(Collectors.toList());
Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
Options options = (Options)Capability.enrich(this.options, this.capabilities);
Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
// 返回一个可以生成FeignClient动态代理的对象
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
public <T> T newInstance(Target<T> target) {
//根据 target对象创建对应方法的SpringMVC注解的MethodHandler,创建的MethodHandler是SynchronousMethodHandler
//所以在方法动态代理调用是,主要还是调用SynchronousMethodHandler 的invoke方法
Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
// 用于存储方法与解析SpringMVC注解的MethodHandler
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
// 当 target 是 Object对象是,则会用到该list
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
// 获取当前接口所有方法集合
Method[] var5 = target.type().getMethods();
int var6 = var5.length;
// 循环所有方法集合
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
// 判断当前的方法是存在于对象中还是接口中
if (method.getDeclaringClass() != Object.class) {
// 当前的方法在对象(Object)中
if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
// 当前方法在接口(interface)中
//并且对该方法解析SpringMVC注解的nameToHandler放一起
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
}
// 通过动态代理构造工厂 InvocationHandlerFactory 创建一个 FeignInvocationHandler动态代理 Handler
InvocationHandler handler = this.factory.create(target, methodToHandler);
// 生成一个动态代理对象
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
// 若是对象中的方法则通过默认的 DefaultMethodHandler 绑定到创建成功的proxy动态代理上面
Iterator var12 = defaultMethodHandlers.iterator();
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
//返回一个该 类/接口 的动态代理
return proxy;
}
从上面的代码中可以看到,当 调用FeignClientFactoryBean#getObject
方法时,其实获取到的是一个已经被 Feign
使用 Java
原生的动态代理进行代理过的对象即 FeignInvocationHandler
对象,这时候就知道我们在Spring
自动注入 Feign Client
的时候其实注入的就是 FeignInvocationHandler
对象,接下来,我们来看一下,当我们调用 Feign Client
请求服务接口时的调用流程。
4、OpenFeign 动态代理调用Eureka服务接口
FeignInvocationHandler
动态代理核心方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!"equals".equals(method.getName())) {
if ("hashCode".equals(method.getName())) {
return this.hashCode();
} else {
// 主要是这里通过创建时放入的methodToHandler,根据动态代理传过 // 来的方法获取该方法的MethodHandler,进行调用Eureka服务
return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
}
} else {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return this.equals(otherHandler);
} catch (IllegalArgumentException var5) {
return false;
}
}
}
SynchronousMethodHandler
方法动态代理执行源码
public Object invoke(Object[] argv) throws Throwable {
// 根据方法的SpringMVC注解进行解析请求,创建RequestTemplate
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
// 获取请求超时等参数
Options options = this.findOptions(argv);
// 获取重试机制
Retryer retryer = this.retryer.clone();
while(true) {
try {
// 执行http请求调用
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
// feign 重试机制
RetryableException e = var9;
try {
retryer.continueOrPropagate(e);
} catch (RetryableException var8) {
Throwable cause = var8.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var8;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
//请求数据与解码响应数据
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
//执行请求的一些Interceptor拦截器
Request request = this.targetRequest(template);
//开始请求时间
long start = System.nanoTime();
Response response;
try {
//通过 FeignBlockingLoadBalancerClient进行执行eureka接口调用
response = this.client.execute(request, options);
//根据response响应数据builder一下整理返回数据
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var12) {
// 如果Http请求出现IOException就是通信异常,就会throw FeignException 异常
// 在调用前已经 try catch捕获到FeignException该,并且也有相关的重试机制
throw FeignException.errorExecuting(request, var12);
}
// 获取请求调用消耗时长
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (this.decoder != null) {
// 根据Feign的解码器将返回的数据处理成该方法的返回类型
return this.decoder.decode(response, this.metadata.returnType());
} else {
// 如果没有自定义的解码器,就直接调用当前异步返回方法类型其核心还是使用默认的Decoder
CompletableFuture<Object> resultFuture = new CompletableFuture();
this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone()) {
throw new IllegalStateException("Response handling not done");
} else {
return resultFuture.join();
}
} catch (CompletionException var13) {
Throwable cause = var13.getCause();
if (cause != null) {
throw cause;
} else {
throw var13;
}
}
}
}
根据上面的代码能够看到,其实调用的方式还是使用的 FeignBlockingLoadBalancerClient
而 FeignBlockingLoadBalancerClient
也属于 LoadBalancer
组件的范畴,所以在进行Eureka
服务地址选择时,还是走的 LoadBalancer
的那一套东西,只不过在获取到相应的请求地址时,会生成一个 Request
请求,最核心还是调用 feign.Client.Default#convertResponse
方法进行http调用
5、总结
从上面的源码解析,我们能够看到,OpenFeign
的整体流程是
1、先根据 @EnableFeignClients
注解所在的包名进行扫描下面所有的 @FeignClient
接口,并且配置相关配置信息参数
2、获取到所有标注 @FeignClient
接口进行循环创建 BeanDefinitionBuilder Spring Bean
3、每个BeanDefinitionBuilder
都是一个 FactoryBean
所以就会有返回一个通过 JDK 动态代理
生成的对象进行返回给 Spring
容器,而在通过Spring
注入时,就会将动态代理过后的对象注入进去。即、FeignInvocationHandler
4、在调用 Feign Client
接口时通过 FeignInvocationHandler
动态代理对象进行远程调用 Eureka
远程服务,主要是根据 FeignBlockingLoadBalancerClient
通过 LoadBalancer
组件进行获取对应的服务请求地址并且组装成一个 Request
,通过 feign.Client.Default#convertResponse
方法进行Http
调用
5、在调用结束后通过返回的数据进行 Decoder
解码返回参数,返回给调用方法
自此以上 OpenFeign
主要流程已全部解析完毕
标签:FeignClient,Feign,OpenFeign,builder,源码,context,小许,null,class 来源: https://blog.csdn.net/qq_38100149/article/details/118369219