【OpenFeign】【源码+图解】【二】注册OpenFeign接口的实例


目录

  • 4. FeignClient的配置信息
  • 4.1 FeignClientFactoryBean
  • 4.2 FeignClientFactoryBean.getObject()
  • 4.2.1 FeignContext
  • 4.2.2 Feign.Builder
  • 4.2.2.1 Bean的配置方式
  • YML
  • @FeignClient.configuration
  • @EnableFeignClients.defaultConfiguration
  • Targeter


4. FeignClient的配置信息

上一节中利用FeignClientFactoryBean创建@FeignClient接口的实例client,使用的时候通过**FeignClientFactoryBean.getObject()获得。本节分两步介绍:1、认识FeignClientFactoryBean ** 2、FeignClientFactoryBean.getObject()

4.1 FeignClientFactoryBean

先看下它的类图

openfeign关闭负载均衡_赋值

先看下它的属性,它的属性基本是从**@FeignClient的属性赋值过来的,参考前面一节;再看它实现的接口,其中FactoryBean**的getObject则是分析的重点

4.2 FeignClientFactoryBean.getObject()

先看下其总体流程图

openfeign关闭负载均衡_赋值_02

整个流程主要分两部分

  1. 从FeignContext获取各种beans
  2. 将beans赋值给Feign.Builder,主要是承接yml文件中feign的配置,或者@FeignClient.configuration的配置,或者@EnableFeignClients.defaultConfiguration
  3. 通过工具类Targeter类创建instance

接下来我们逐一分析

4.2.1 FeignContext

openfeign关闭负载均衡_openfeign_03

如果看过前面关于LoadBalance的3.2节文章就会了解NamedContextFactory,这里不再详细分析。

对于FeignContext它是在Spring启动的时候通过FeignAutoConfiguration注入容器的
看下FeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ 
    FeignClientProperties.class,  // yml文件中feign.client.*的配置
    FeignHttpClientProperties.class, // yml文件中feign.httpclient.*的配置
	FeignEncoderProperties.class }) // yml文件中feign.encoder.*的配置
public class FeignAutoConfiguration {
    @Autowired(required = false)
    // configurations即@EnableFeignClients.defaultConfiguration和@FeignClient.configuration,参考FeignClientsRegistrar的流程图
	private List<FeignClientSpecification> configurations = new ArrayList<>();

	@Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
        // 设置configurations
		context.setConfigurations(this.configurations);
		return context;
	}
}

FeignClientFactoryBean.getObject()方法中使用了大量的getInstance(String contextName, String beanName, Class<T> type)方法,过程不再详述(参考LoadBalance的3.2节),大致过程是:

  1. 首次根据configurations创建名为contextName的ApplicationContext,并存放到FeignContext,之后直接取用
  2. 从ApplicationContext获取beanName的bean

FeignClientFactoryBean.getObject()主要是获取了以下的类:

  1. Feign.Builder
  2. Client
  3. FeignBuilderCustomizer
  4. Targeter
  5. FeignClientConfigurer
  6. FeignLoggerFactory
  7. Encoder(SpringEncoder)
  8. Decoder(OptionalDecoder)
  9. Contract(SpringMvcContract)
  10. Request.Options
  11. Logger.Level
  12. Retryer
  13. ErrorDecoder
  14. FeignErrorDecoderFactory
  15. RequestInterceptor
  16. QueryMapEncoder
  17. ExceptionPropagationPolicy
  18. Capability

这些类可以自配置

4.2.2 Feign.Builder

先看下它的类图

openfeign关闭负载均衡_赋值_04

FeignClientFactoryBean.getObject()中对Feign.Builder赋值结束后默认的值如下

openfeign关闭负载均衡_openfeign_05

对于Builder里面的类,如RequestInterceptor等的默认配置是通过下列Configuration注入的

openfeign关闭负载均衡_赋值_06

当然,也可以自己配置,实现相应的接口,然后通过yml文件配置,如下图

openfeign关闭负载均衡_openfeign_07

4.2.2.1 Bean的配置方式

Contract为例,三种方式:YML、@FeignClient.configuration、@EnableFeignClients.defaultConfiguration

YML

openfeign关闭负载均衡_赋值_08

@FeignClient.configuration
  1. 实现接口
public class MyContract implements Contract {
    @Override
    public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
        return new ArrayList<>();
    }
}
  1. 添加配置文件
public class MyConfiguration {
    @Bean
    @Primary
    MyContract myContract() {
        return new MyContract();
    }
}
  1. 添加到@FeignClient
@FeignClient(value = "product", configuration = MyConfiguration.class)
public interface ProductFeignClient{
}
@EnableFeignClients.defaultConfiguration
  1. 实现接口
public class MyDefaultContract implements Contract {
    @Override
    public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
        return new ArrayList<>();
    }
}
  1. 添加配置文件
public class MyDefaultConfiguration {

    @Bean
    MyDefaultContract defaultContract() {
        return new MyDefaultContract();
    }
}
  1. 添加到@FeignClient
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = MyDefaultConfiguration.class)
public class FeignClientApplication {

	public static void main(String[] args) {
		SpringApplication.run(FeignClientApplication.class, args);
	}
}

注意

  1. 注入的顺序:YML > @FeignClient.configuration , @EnableFeignClients.defaultConfiguration
  2. @FeignClient.configuration与@EnableFeignClients.defaultConfiguration同时使用时要注明谁是Primary,否则会报错

Targeter

先看下Targeter的类图,了解下功能

openfeign关闭负载均衡_微服务_09

Targeter的作用主要是中转站,默认实现是DefaultTargeter,先看下FeignClientFactoryBean.getObject()中的源码调用

public class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
    @Override
	public Object getObject() {
        // 1
		return getTarget();
	}
    
    <T> T getTarget() {
        ......
        if (!StringUtils.hasText(url)) {
            ......
            // 2
			return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
		}
    }
    
    protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
		Client client = getOptional(context, Client.class);
		if (client != null) {
			builder.client(client);
			applyBuildCustomizers(context, builder);
            // 获取Targeter实例,默认DefaultTargeter
			Targeter targeter = get(context, Targeter.class);
            // 这里的target为HardCodedTarget
            // 3 下一章节的入口
			return targeter.target(this, builder, context, target);
		}
        ......
	}
}