Spring|Spring Boot Feign调用探究(二)

今天我们具体分析FeignClientFactoryBean的作用,分析之前先贴出来@EnableFeignClients的部分细节:

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

为什么要贴出@EnableFeignClients呢?因为这个FeignClientsRegistrar类是我们解析Feign类的入口,下面的代码块是FeignClientsRegistrar的部分细节:
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map attributes) { String className = annotationMetadata.getClassName(); //赋值FeignClientFactoryBean类 BeanDefinitionBuilder definition = BeanDefinitionBuilder .genericBeanDefinition(FeignClientFactoryBean.class); }

由上面的代码片段,可以看到 FeignClientsRegistrar 会解析@EnableFeignClients注解,然后在构建BeanDefinition过程中,会注入FeignClientFactoryBean类,这样一来在后续实例化我们声明的Feign类型的Bean过程中,会根据我们配置的name/value,url等信息,实例化合适的FeignClient类。
现在我们大致了解了FeignClientFactoryBean的作用。然后再看一下FeignClientFactoryBean的getObject():
@Override public Object getObject() throws Exception { FeignContext context = applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); //如果我们没有配置url if (!StringUtils.hasText(this.url)) { String url; if (!this.name.startsWith("http")) { url = "http://" + this.name; } else { url = this.name; } url += cleanPath(); //调用负载均衡逻辑 return loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, url)); } //如果配置了url if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { this.url = "http://" + this.url; } String url = this.url + cleanPath(); Client client = getOptional(context, Client.class); if (client != null) { if (client instanceof LoadBalancerFeignClient) { // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap client = ((LoadBalancerFeignClient)client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); //底层会构建HttpUrlConnection对象,其实也就是普通的http请求 return targeter.target(this, builder, context, new HardCodedTarget<>( this.type, this.name, url)); }

现在有没有恍然大迷瞪?!这个FeignClientFactoryBean决定了要创建哪一种的FeignClient对象!
那么问题来了,FeignClientsRegistrar类实现了ImportBeanDefinitionRegistrar接口,这个接口是干嘛用的呢?
Spring|Spring Boot Feign调用探究(二)
文章图片
图1
1:注册beanFactoy处理器。
2:解析所有的bean(ConfigurationClass)。
3:执行FeignClientsRegistrar类的 registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry)方法;
下面两个图是获取启动类中配置的注解信息,并且加载springbean容器中:因为我们的启动类配置了@EnableFeignClients,@EnableFeignClients又引入了FeignClientsRegistrar.class,所以此时会加载FeignClientsRegistrar类;
Spring|Spring Boot Feign调用探究(二)
文章图片
图2
Spring|Spring Boot Feign调用探究(二)
文章图片
图3
【Spring|Spring Boot Feign调用探究(二)】下面的代码块是具体调用的逻辑:
//加载启动类的中注册器 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

    推荐阅读