前面幾大章節(jié)我們分析和總結(jié)了Ribbon負(fù)載均衡陋气、Hystrix熔斷巩趁、Zuul網(wǎng)關(guān)這三大SpringBoot應(yīng)用必不可少的利器议慰,也在前面幾節(jié)分析了Feign客戶端的注冊過程和Feign如何做到版本兼容相關(guān)的講解别凹,筆者想了想還是覺得少了點(diǎn)東西番川,覺得應(yīng)該把Feign的整個(gè)調(diào)用鏈都進(jìn)行總結(jié)一遍颁督,后續(xù)我們擴(kuò)展Feign時(shí)候才能游刃有余沉御,所以本節(jié)將分析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<FeignClientSpecification> configurations = new ArrayList<>();
//注冊Feign特性描述
@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}
//創(chuàng)建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這個(gè)入口配置類中我們只貼非常重要的片段怀跛,List<FeignClientSpecification> configurations這個(gè)@FeignClient注解生成的FeignClientSpecification對象可參閱Spring Cloud Feign 分析(一)之FeignClient注冊過程吻谋,F(xiàn)eignContext與HystrixTargeter對象我們開始逐一講解。
FeignContext
/**
* 對外提供的實(shí)例工廠類戒祠,為每個(gè)Feign客戶端創(chuàng)建一個(gè)ApplicationContext得哆,并從中提取所需的Bean
*/
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
//獲取實(shí)例
@Nullable
public <T> T getInstanceWithoutAncestors(String name, Class<T> type) {
try {
return BeanFactoryUtils.beanOfType(getContext(name), type);
}
catch (BeansException ex) {
return null;
}
}
//獲取實(shí)例集合
@Nullable
public <T> Map<String, T> getInstancesWithoutAncestors(String name, Class<T> type) {
return getContext(name).getBeansOfType(type);
}
}
外部主要通過該工廠類獲取FeignClient客戶端實(shí)例贩据,通過繼承NamedContextFactory會(huì)為每一個(gè)FeignClient客戶端都創(chuàng)建一個(gè)ApplicationContext栋操,以便于從每個(gè)FeignClient的ApplicationContext獲取指定的Bean對象闸餐,這個(gè)工廠類最主要的特征就是隔離了每個(gè)FeignClient,每個(gè)FeignClient客戶端都有自己的一個(gè)ApplicationContext上下文矾芙。
NamedContextFactory
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
implements DisposableBean, ApplicationContextAware {
......
//設(shè)置@FeignClient注解生成的FeignClientSpecification對象
public void setConfigurations(List<C> configurations) {
for (C client : configurations) {
this.configurations.put(client.getName(), client);
}
}
......
//獲取@FeignClient對應(yīng)的ApplicationContext 應(yīng)用上下文
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
//如果在contexts上下文Map中找到則創(chuàng)建一個(gè)@FeignClient對應(yīng)的應(yīng)用上下文
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
//創(chuàng)建FeignClient的ApplicationContext應(yīng)用上下文
protected AnnotationConfigApplicationContext createContext(String name) {
//創(chuàng)建一個(gè)ApplicationContext
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
if (this.configurations.containsKey(name)) {
//如果有配置文件則注冊到當(dāng)前ApplicationContext應(yīng)用上下文中
//如果@FeignClients配置了configuration則將被注冊到當(dāng)前ApplicationContext應(yīng)用上下文中
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
//@EnableFeignClients的defaultConfiguration配置將會(huì)被注冊到ApplicationContext中
for (Map.Entry<String, C> 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.<String, Object>singletonMap(this.propertyName, name)));
//設(shè)置父類ApplicationContext,這樣可以訪問父類Bean
if (this.parent != null) {
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
//設(shè)置DisplayName屬性剔宪,格式為:FeignContext-FeignClient客戶端name/value屬性
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}
//獲取實(shí)例
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
try {
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException e) {
// ignore
}
return null;
}
......
}
通過NamedContextFactory中的代碼片段以及注釋信息拂铡,我們更佳直觀的看出,和命名空間很像葱绒,每一個(gè)@FeignClient對應(yīng)的FeignClientSpecification對象都會(huì)生成一個(gè)專屬于這個(gè)@FeignClient的一個(gè)應(yīng)用上下文ApplicationContext失球,起到了隔離作用黔牵,生成ApplicationContext應(yīng)用上下文的步驟如下:
- 將@FeignClient注解生成的FeignClientSpecification對象添加到configurations配置Map中
- 根據(jù)@FeignClient的name阶剑、value屬性生成一個(gè)ApplicationContext應(yīng)用上下文
- 將@FeignClients中的configuration配置注冊到當(dāng)前ApplicationContext上下文中
- 將@EnableFeignClients的defaultConfiguration配置注冊到當(dāng)前上下文中
- 注冊屬性配置類、FeignClientsConfiguration客戶端配置類
- 添加MapPropertySource屬性配置
- 設(shè)置父類ApplicationContext,這樣可以訪問父類Bean
- 將當(dāng)前創(chuàng)建的ApplicationContext添加到上下文contexts中,每個(gè)@FeignClient都會(huì)生成一個(gè)ApplicationContext上下文,起隔離的作用
- 對外提供各種getInstance獲取實(shí)例方法
上面我們分析了FeignContext這個(gè)上下文,為每個(gè)@FeignClient客戶端創(chuàng)建一個(gè)ApplicationContext,外部通過這個(gè)工廠類獲取所需要的實(shí)例Bean,那下面我們繼續(xù)聊聊一個(gè)非常重要的目標(biāo)執(zhí)行類,HystrixTargeter具備了熔斷能力的執(zhí)行類铭若!
HystrixTargeter
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
//是否為Feign.Builder 類型镜雨,若不是則直接創(chuàng)建代理對象并執(zhí)行
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
//轉(zhuǎn)換為HystrixFeign.Builder類型
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
//獲取上下文id颓影,其實(shí)就是獲取的@FeignClient注解的name、value屬性值
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
//獲取SetterFactory,主要是HystrixCommand的groupKey、commandKey參數(shù),默認(rèn)setterFactory為空
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
//setterFactory不為空就設(shè)置
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
//獲取降級方法,默認(rèn)為void初始狀態(tài)
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
//如果有設(shè)置了fallback偷遗,則使用
return targetWithFallback(name, context, target, builder, fallback);
}
//獲取降級工廠類FallbackFactory,默認(rèn)為void初始狀態(tài)
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
//調(diào)用HystrixFeign#build()
return feign.target(target);
}
//具有FallbackFactory的目標(biāo)執(zhí)行類
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
//獲取降級工廠實(shí)例
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
//返回具有FallbackFactory的代理實(shí)例
return builder.target(target, fallbackFactory);
}
//具有Fallback的目標(biāo)執(zhí)行類
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
//獲取降級實(shí)例
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
//返回具有fallback的代理實(shí)例
return builder.target(target, fallbackInstance);
}
//返回指定類型的實(shí)例
private <T> T getFromContext(String fallbackMechanism, String feignClientName,
FeignContext context, Class<?> beanType, Class<T> 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;
}
//根據(jù)@FeignClient注解的name、value屬性值獲取對應(yīng)beanType實(shí)例
private <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
}
通過對HystrixTargeter的講解骏令,我們看到邏輯逐漸復(fù)雜起來摘昌,比較直觀的能看出這個(gè)類具備了Hystrix熔斷功能,再簡單點(diǎn),其實(shí)我們能這樣理解:HystrixTargeter會(huì)返回一個(gè)具備Hystrix熔斷功能的代理對象生真,內(nèi)部執(zhí)行順序就是請求先經(jīng)由Hystrix,然后再由LoadBalancerFeignClient進(jìn)行負(fù)載均衡請求最終的結(jié)果斤斧!
這里我們只是簡單的總結(jié)了HystrixTargeter類的作用,feign.target(target)這一句之后的邏輯相對更佳復(fù)雜看彼,及生成代理這個(gè)過程和代理內(nèi)部又做了哪些事情,將會(huì)放在后續(xù)的文章中進(jìn)行總結(jié)和講解逊脯!