上一篇中我们已经提到,对于被@FeignClients注解的接口,我们会根据其属性在IOC容器里注入一个FeignClientFactoryBean,而FeignClientFactoryBean实现了FactoryBean接口,因此实际上我们对该bean进行初始化后得到的是其getObject的返回值。这也是我们能够通过类似于调用服务的方法实现http请求发送的关键所在。
在了解getObject之前,我们先看一下FeignAutoConfiguration类,可以看出这是一个自动配置类。关于自动配置的内容可以通过EnableAutoConfiguration源码解析参考了解。
在FeignAutoConfiguration里通过@bean往IOC容器里注入了不少的bean,我们先了解一下FeignContext类,这是实现feign的一个相当关键的类。
@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
//省略其它方法
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
}
进入feignContext类,该类是泛型类NamedContextFactory的子类,除了构造方法外没有其它的额外属性和方法。
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
}
接着点击super方法进入NamedContextFactory类,构造方法中初始化了defaultConfigType 、propertySourceName 和 propertyName 的属性值。
public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;
}
接着看setConfigurations方法,可以看出其将IOC容器中的所有FeignClientSpecification类以其name属性为key值组成了Map结构,并作为configurations 属性。
private Map<String, C> configurations = new ConcurrentHashMap<>();
public void setConfigurations(List<C> configurations) {
for (C client : configurations) {
this.configurations.put(client.getName(), client);
}
}
除此之外,我们还应该注意到NamedContextFactory类实现了ApplicationContextAware接口,并将当前的spring上下文applicationContext设置为parent属性。
另外,NamedContextFactory类还有一个极其重要的属性contexts,feign中的每一个client对应AnnotationConfigApplicationContext,contexts的key值就是client的name值,这一点和configurations的key值吻合。
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
getContext作为根据client的name获取applicationContext值的方法,和其它从缓存里获取值的方法没有什么区别。先判断缓存是否存在,如果不存在将生成对应的context并放入缓存,然后从缓存里进行获取。
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));