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 Boot Feign调用探究(二)_第1张图片图1
1:注册beanFactoy处理器。
2:解析所有的bean(ConfigurationClass)。
3:执行FeignClientsRegistrar类的 registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry)方法;

下面两个图是获取启动类中配置的注解信息,并且加载springbean容器中:因为我们的启动类配置了@EnableFeignClients,@EnableFeignClients又引入了FeignClientsRegistrar.class,所以此时会加载FeignClientsRegistrar类;

image.png图2
image.png图3

下面的代码块是具体调用的逻辑:

//加载启动类的中注册器
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

你可能感兴趣的:(Spring Boot Feign调用探究(二))