【OpenFeign】【源码+图解】【二】注册OpenFeign接口的实例
目录
4. FeignClient的配置信息
上一节中利用FeignClientFactoryBean创建@FeignClient接口的实例client,使用的时候通过**FeignClientFactoryBean.getObject()获得。本节分两步介绍:1、认识FeignClientFactoryBean ** 2、FeignClientFactoryBean.getObject()
4.1 FeignClientFactoryBean
先看下它的类图
先看下它的属性,它的属性基本是从**@FeignClient的属性赋值过来的,参考前面一节;再看它实现的接口,其中FactoryBean**的getObject
则是分析的重点
4.2 FeignClientFactoryBean.getObject()
先看下其总体流程图
整个流程主要分两部分
- 从FeignContext获取各种beans
- 将beans赋值给Feign.Builder,主要是承接yml文件中feign的配置,或者@FeignClient.configuration的配置,或者@EnableFeignClients.defaultConfiguration
- 通过工具类Targeter类创建instance
接下来我们逐一分析
4.2.1 FeignContext
如果看过前面关于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节),大致过程是:
- 首次根据configurations创建名为contextName的ApplicationContext,并存放到FeignContext,之后直接取用
- 从ApplicationContext获取beanName的bean
FeignClientFactoryBean.getObject()主要是获取了以下的类:
- Feign.Builder
- Client
- FeignBuilderCustomizer
- Targeter
- FeignClientConfigurer
- FeignLoggerFactory
- Encoder(SpringEncoder)
- Decoder(OptionalDecoder)
- Contract(SpringMvcContract)
- Request.Options
- Logger.Level
- Retryer
- ErrorDecoder
- FeignErrorDecoderFactory
- RequestInterceptor
- QueryMapEncoder
- ExceptionPropagationPolicy
- Capability
这些类可以自配置
4.2.2 Feign.Builder
先看下它的类图
在FeignClientFactoryBean.getObject()
中对Feign.Builder赋值结束后默认的值如下
对于Builder里面的类,如RequestInterceptor等的默认配置是通过下列Configuration注入的
当然,也可以自己配置,实现相应的接口,然后通过yml文件配置,如下图
4.2.2.1 Bean的配置方式
以Contract为例,三种方式:YML、@FeignClient.configuration、@EnableFeignClients.defaultConfiguration
YML
@FeignClient.configuration
-
实现接口
public class MyContract implements Contract { @Override public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) { return new ArrayList<>(); } }
-
添加配置文件
public class MyConfiguration { @Bean @Primary MyContract myContract() { return new MyContract(); } }
-
添加到@FeignClient
@FeignClient(value = "product", configuration = MyConfiguration.class) public interface ProductFeignClient{ }
@EnableFeignClients.defaultConfiguration
-
实现接口
public class MyDefaultContract implements Contract { @Override public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) { return new ArrayList<>(); } }
-
添加配置文件
public class MyDefaultConfiguration { @Bean MyDefaultContract defaultContract() { return new MyDefaultContract(); } }
-
添加到@FeignClient
@SpringBootApplication @EnableFeignClients(defaultConfiguration = MyDefaultConfiguration.class) public class FeignClientApplication { public static void main(String[] args) { SpringApplication.run(FeignClientApplication.class, args); } }
注意
- 注入的顺序:YML > @FeignClient.configuration , @EnableFeignClients.defaultConfiguration
- @FeignClient.configuration与@EnableFeignClients.defaultConfiguration同时使用时要注明谁是Primary,否则会报错
Targeter
先看下Targeter的类图,了解下功能
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);
}
......
}
}