前面几大章节我们分析和总结了Ribbon负载均衡、Hystrix熔断、Zuul网关这三大SpringBoot应用必不可少的利器,也在前面几节分析了Feign客户端的注册过程和Feign如何做到版本兼容相关的讲解,笔者想了想还是觉得少了点东西,觉得应该把Feign的整个调用链都进行总结一遍,后续我们扩展Feign时候才能游刃有余,所以本节将分析FeignAutoConfiguration入口配置,咱们分析的Feign版本为10.10.1版本,基于SpringBoot2.3.9.RELEASE!
FeignAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
//@FeignClient注解生成的FeignClientSpecification对象
@Autowired(required = false)
private List configurations = new ArrayList<>();
//注册Feign特性描述
@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}
//创建Feign上下文
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
......
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
//注册具有Hystrix熔断功能的代理类
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
......
}
在FeignAutoConfiguration这个入口配置类中我们只贴非常重要的片段,List
FeignContext
/**
* 对外提供的实例工厂类,为每个Feign客户端创建一个ApplicationContext,并从中提取所需的Bean
*/
public class FeignContext extends NamedContextFactory {
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
//获取实例
@Nullable
public T getInstanceWithoutAncestors(String name, Class type) {
try {
return BeanFactoryUtils.beanOfType(getContext(name), type);
}
catch (BeansException ex) {
return null;
}
}
//获取实例集合
@Nullable
public Map getInstancesWithoutAncestors(String name, Class type) {
return getContext(name).getBeansOfType(type);
}
}
外部主要通过该工厂类获取FeignClient客户端实例,通过继承NamedContextFactory会为每一个FeignClient客户端都创建一个ApplicationContext,以便于从每个FeignClient的ApplicationContext获取指定的Bean对象,这个工厂类最主要的特征就是隔离了每个FeignClient,每个FeignClient客户端都有自己的一个ApplicationContext上下文。
NamedContextFactory
public abstract class NamedContextFactory
implements DisposableBean, ApplicationContextAware {
......
//设置@FeignClient注解生成的FeignClientSpecification对象
public void setConfigurations(List configurations) {
for (C client : configurations) {
this.configurations.put(client.getName(), client);
}
}
......
//获取@FeignClient对应的ApplicationContext 应用上下文
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
//如果在contexts上下文Map中找到则创建一个@FeignClient对应的应用上下文
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
//创建FeignClient的ApplicationContext应用上下文
protected AnnotationConfigApplicationContext createContext(String name) {
//创建一个ApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
if (this.configurations.containsKey(name)) {
//如果有配置文件则注册到当前ApplicationContext应用上下文中
//如果@FeignClients配置了configuration则将被注册到当前ApplicationContext应用上下文中
for (Class> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
//@EnableFeignClients的defaultConfiguration配置将会被注册到ApplicationContext中
for (Map.Entry entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
//注册属性配置、FeignClientsConfiguration配置类
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
//添加属性
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.singletonMap(this.propertyName, name)));
//设置父类ApplicationContext,这样可以访问父类Bean
if (this.parent != null) {
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
//设置DisplayName属性,格式为:FeignContext-FeignClient客户端name/value属性
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
//获取实例
public T getInstance(String name, Class type) {
AnnotationConfigApplicationContext context = getContext(name);
try {
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException e) {
// ignore
}
return null;
}
......
}
通过NamedContextFactory中的代码片段以及注释信息,我们更佳直观的看出,和命名空间很像,每一个@FeignClient对应的FeignClientSpecification对象都会生成一个专属于这个@FeignClient的一个应用上下文ApplicationContext,起到了隔离作用,生成ApplicationContext应用上下文的步骤如下:
- 将@FeignClient注解生成的FeignClientSpecification对象添加到configurations配置Map中
- 根据@FeignClient的name、value属性生成一个ApplicationContext应用上下文
- 将@FeignClients中的configuration配置注册到当前ApplicationContext上下文中
- 将@EnableFeignClients的defaultConfiguration配置注册到当前上下文中
- 注册属性配置类、FeignClientsConfiguration客户端配置类
- 添加MapPropertySource属性配置
- 设置父类ApplicationContext,这样可以访问父类Bean
- 将当前创建的ApplicationContext添加到上下文contexts中,每个@FeignClient都会生成一个ApplicationContext上下文,起隔离的作用
- 对外提供各种getInstance获取实例方法
上面我们分析了FeignContext这个上下文,为每个@FeignClient客户端创建一个ApplicationContext,外部通过这个工厂类获取所需要的实例Bean,那下面我们继续聊聊一个非常重要的目标执行类,HystrixTargeter具备了熔断能力的执行类!
HystrixTargeter
class HystrixTargeter implements Targeter {
@Override
public T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget target) {
//是否为Feign.Builder 类型,若不是则直接创建代理对象并执行
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
//转换为HystrixFeign.Builder类型
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
//获取上下文id,其实就是获取的@FeignClient注解的name、value属性值
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
//获取SetterFactory,主要是HystrixCommand的groupKey、commandKey参数,默认setterFactory为空
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
//setterFactory不为空就设置
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
//获取降级方法,默认为void初始状态
Class> fallback = factory.getFallback();
if (fallback != void.class) {
//如果有设置了fallback,则使用
return targetWithFallback(name, context, target, builder, fallback);
}
//获取降级工厂类FallbackFactory,默认为void初始状态
Class> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
//调用HystrixFeign#build()
return feign.target(target);
}
//具有FallbackFactory的目标执行类
private T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget target, HystrixFeign.Builder builder,
Class> fallbackFactoryClass) {
//获取降级工厂实例
FallbackFactory extends T> fallbackFactory = (FallbackFactory extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
//返回具有FallbackFactory的代理实例
return builder.target(target, fallbackFactory);
}
//具有Fallback的目标执行类
private T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget target, HystrixFeign.Builder builder,
Class> fallback) {
//获取降级实例
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
//返回具有fallback的代理实例
return builder.target(target, fallbackInstance);
}
//返回指定类型的实例
private T getFromContext(String fallbackMechanism, String feignClientName,
FeignContext context, Class> beanType, Class targetType) {
Object fallbackInstance = context.getInstance(feignClientName, beanType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No " + fallbackMechanism
+ " instance of type %s found for feign client %s",
beanType, feignClientName));
}
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(String.format("Incompatible "
+ fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
return (T) fallbackInstance;
}
//根据@FeignClient注解的name、value属性值获取对应beanType实例
private T getOptional(String feignClientName, FeignContext context,
Class beanType) {
return context.getInstance(feignClientName, beanType);
}
}
通过对HystrixTargeter的讲解,我们看到逻辑逐渐复杂起来,比较直观的能看出这个类具备了Hystrix熔断功能,再简单点,其实我们能这样理解:HystrixTargeter会返回一个具备Hystrix熔断功能的代理对象,内部执行顺序就是请求先经由Hystrix,然后再由LoadBalancerFeignClient进行负载均衡请求最终的结果!
这里我们只是简单的总结了HystrixTargeter类的作用,feign.target(target)这一句之后的逻辑相对更佳复杂,及生成代理这个过程和代理内部又做了哪些事情,将会放在后续的文章中进行总结和讲解!