本文約2千字,主要知識(shí)
在鳳慶等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都做網(wǎng)站、成都網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站制作,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),全網(wǎng)整合營(yíng)銷推廣,成都外貿(mào)網(wǎng)站建設(shè),鳳慶網(wǎng)站建設(shè)費(fèi)用合理。FeignClient
的注冊(cè)在使用Spring Cloud時(shí),經(jīng)常使用OpenFeign 作為遠(yuǎn)程服務(wù)調(diào)用的類庫(kù);
Feign 是一種聲明式服務(wù)調(diào)用組件,它在 RestTemplate 的基礎(chǔ)上做了進(jìn)一步的封裝。通過(guò) Feign,我們只需要聲明一個(gè)接口并通過(guò)注解進(jìn)行簡(jiǎn)單的配置(類似于 Dao 接口上面的 Mapper 注解一樣)即可實(shí)現(xiàn)對(duì) HTTP 接口的綁定。通過(guò) Feign,我們可以像調(diào)用本地方法一樣來(lái)調(diào)用遠(yuǎn)程服務(wù),而完全感覺(jué)不到這是在進(jìn)行遠(yuǎn)程調(diào)用。
Spring Boot3中,也引入了一套全新的聲明式HTTP調(diào)用,這也可說(shuō)明聲明式的調(diào)用方式收到足夠的青睞。
Spring Boot : 2.7.6
spring-cloud:2021.0.5
OpenFeign-core : 11.10
jdk:17
https://github.com/Fudeveloper/openfeign-demo.git
以下為示例工程的主要類
@FeignClient(name = "api-proxy"
,url = "http://127.0.0.1:8080"
,configuration = MyApiConfiguration.class)
public interface MyApi {@GetMapping("/test/my")
String testForGet();
}
public class MyApiConfiguration {@Bean
public Encoder myEncoder() {return new Encoder() {@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {System.out.println("trigger myEncoder#encode");
}
};
}
}
參考資料OpenFeign 的使用文檔
FeignClient BeanDefinition的注冊(cè)按照國(guó)際慣例,要啟用SpringBoot
的某個(gè)組件,需要先標(biāo)注@EnableXXX
注解;OpenFeign 也不例外,其注解類@EnableFeignClients
主要內(nèi)容如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 核心:導(dǎo)入FeignClientsRegistrar
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients { String[] basePackages() default {};
Class>[] clients() default {};
// ...
}
其中,最核心的動(dòng)作是@Import(FeignClientsRegistrar.class)
FeignClientsRegistrar
實(shí)現(xiàn)了ImportBeanDefinitionRegistrar
接口,實(shí)現(xiàn)了一套自定義的BeanDefinition
注冊(cè)流程。在SpringapplicationContext
容器refresh
的過(guò)程中(具體為invokeBeanFactoryPostProcessors->postProcessBeanDefinitionRegistry->processConfigBeanDefinitions
)時(shí),將調(diào)用FeignClientsRegistrar#registerBeanDefinitions
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 注冊(cè) 默認(rèn)配置
registerDefaultConfiguration(metadata, registry);
// 注冊(cè) FeignClient
registerFeignClients(metadata, registry);
}
}
registerFeignClients
注冊(cè)所有@FeignClientpublic void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 保存clients BeanDefinition 的數(shù)組
LinkedHashSetcandidateComponents = new LinkedHashSet<>();
Mapattrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class>[] clients = attrs == null ? null : (Class>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {// 如果未手動(dòng)指定 clients,則掃描后添加
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
SetbasePackages = getBasePackages(metadata);
for (String basePackage : basePackages) { candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {// 已手動(dòng)指定,直接添加
for (Class>clazz : clients) { candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// ①遍歷所有client,并注冊(cè)
for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) {// ...
// 獲取@FeignClient 標(biāo)注類的屬性
Mapattributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// ② 注冊(cè) ${name}.FeignClientSpecification
registerClientConfiguration(registry, name, attributes.get("configuration"));
// ③ 注冊(cè)client的bean
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
在①處斷點(diǎn)后可以發(fā)現(xiàn),candidateComponents
已裝載我們自定義的FeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Mapattributes) {String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
// 強(qiáng)轉(zhuǎn)
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
// ③.1 構(gòu)建factoryBean
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
// 是否可刷新,從feign.client.refresh-enabled配置解析
factoryBean.setRefreshableClient(isClientRefreshEnabled());
// 將@FeignClinet標(biāo)注類的配置信息構(gòu)建為BeanDefinition
// clazz即為@FeignClinet標(biāo)注類的class
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () ->{ factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) { factoryBean.setFallback(fallback instanceof Class ? (Class>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) { factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
// ③.2 注意此處是從factoryBean 獲取對(duì)象,具體為FeignClientFactoryBean#getObject
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) { qualifiers = new String[] {contextId + "FeignClient" };
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
// 委托給DefaultListableBeanFactory#registerBeanDefinition
// ④ 將@FeignClinet標(biāo)注類注冊(cè)入Spring IOC父容器
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
// 支持Request.Options的refresh 操作,非本章重點(diǎn)
registerOptionsBeanDefinition(registry, contextId);
}
registerDefaultConfiguration
、registerFeignClients
,最終都將調(diào)用registerClientConfiguration
方法。
② 和③ 方法則將實(shí)際操作委托給DefaultListableBeanFactory#registerBeanDefinition
;這涉及到Spring IOC的核心流程,此處不再贅述。在本文中只需記住如下現(xiàn)象。若要了解更多相關(guān)知識(shí),可參考: 《Spring 揭秘》第二部分:Spring IOC容器
![](https://img-blog.csdnimg.cn/img_convert/183575716fde7fefbf7cffe4f2f9da87.png#averageHue=#f0f0f0&crop=0&crop=0&crop=1&crop=1&from=url&height=296&id=BnAcr&margin=[object Object]&originHeight=323&originWidth=775&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=711)
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {// 不存在則加入集合
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
}
在③結(jié)束后,查看DefaultListableBeanFactory#beanDefinitionMap
,可發(fā)現(xiàn)我們自定義的FeignClient已注冊(cè)到IOC 容器中
回看③.1 、③.2的操作,即創(chuàng)建FeignClientFactoryBean
并從中獲取對(duì)象;在Spring中,FactoryBean
是個(gè)舉足輕重的類,可參考:What’s a FactoryBean?
注意③.2的后續(xù)操作:FeignClientFactoryBean#getObject
public class FeignClientFactoryBean implements FactoryBean
根據(jù)Spring Boot的SPI
機(jī)制,可以發(fā)現(xiàn)在IOC 容器中引入了FeignAutoConfiguration
。FeignAutoConfiguration
主要工作是注入一個(gè)FeignContext
,與上方③.3 相呼應(yīng)
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class, FeignEncoderProperties.class })
public class FeignAutoConfiguration {// 注入第一節(jié)中的所有配置
@Autowired(required = false)
private Listconfigurations = new ArrayList<>();
// ③.4 構(gòu)建FeignContext
@Bean
public FeignContext feignContext() {FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
}
在③.4 步驟時(shí),斷點(diǎn)查看configurations,可看到 步驟② 注冊(cè)到Spring IOC容器中的FeignClientSpecification
public class FeignContext extends NamedContextFactory{public FeignContext() {// 注意FeignClientsConfiguration 類,其中存放了默認(rèn)Bean配置
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
}
FeignContext
繼承于NamedContextFactory
:NamedContextFactory 可以創(chuàng)建一個(gè)子容器(或者說(shuō)子上下文)。OpenFeign
則是依賴它來(lái)實(shí)現(xiàn)子容器機(jī)制
的。
在③.5中,多處調(diào)用了FeignClientFactoryBean#get
方法,此方法將調(diào)用NamedContextFactory#createContext
方法創(chuàng)建子容器。
// 子容器 map集合
private Mapcontexts = new ConcurrentHashMap<>();
publicT getInstance(String name, Classtype) {AnnotationConfigApplicationContext context = getContext(name);
try {// 從容器中獲取Bean
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException e) {// ignore
}
return null;
}
protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {// 不存在時(shí),將創(chuàng)建子容器
this.contexts.put(name, createContext(name));
}
}
}
// 從子容器中獲取
return this.contexts.get(name);
}
// 創(chuàng)建子容器的方法
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context;
if (this.parent != null) {// 處理父類信息
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
if (parent instanceof ConfigurableApplicationContext) {beanFactory.setBeanClassLoader(
((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());
}
else {beanFactory.setBeanClassLoader(parent.getClassLoader());
}
context = new AnnotationConfigApplicationContext(beanFactory);
context.setClassLoader(this.parent.getClassLoader());
}
else {// 當(dāng)前已為頂級(jí)
context = new AnnotationConfigApplicationContext();
}
if (this.configurations.containsKey(name)) {for (Class>configuration : this.configurations.get(name).getConfiguration()) {// 處理@FeignClient 中定義的configuration,對(duì)應(yīng)背景中的`MyApiConfiguration`
context.register(configuration);
}
}
for (Map.Entryentry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class>configuration : entry.getValue().getConfiguration()) {context.register(configuration);
}
}
}
context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,
Collections.singletonMap(this.propertyName, name)));
if (this.parent != null) {// 設(shè)置父容器,目的是讓所有bean也使用父容器的上下文信息
context.setParent(this.parent);
}
context.setDisplayName(generateDisplayName(name));
// 和Spring IOC容器相同,走refresh流程
context.refresh();
return context;
}
同樣的,在子容器的refresh 過(guò)程中(具體為finishBeanFactoryInitialization->beanFactory.preInstantiateSingletons
),將初始化子容器所需要的Bean,如背景中定義的MyEncoder
,以及FeignClientsConfiguration
類中定義的名為feignXX
的各個(gè)bean。
之后情況則與IOC 容器初始化過(guò)程類似,此處不再贅述。
子容器初始化完成后,執(zhí)行到③.5.2 處的代碼,其根據(jù)FeignClientProperties、FeignClientConfiguration、父容器配置Feign;其中,FeignClientConfiguration
主要是注入一些默認(rèn)Bean,如下所示
@Configuration(proxyBeanMethods = false)
public class FeignClientsConfiguration {@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
//... 省略其他默認(rèn)Encoder、Capability 等
}
之后,執(zhí)行至④,將@FeignClinet標(biāo)注類,以bean的形式注冊(cè)入Spring IOC父容器。至此,F(xiàn)eignClient的注冊(cè)已完成。
總結(jié)你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
當(dāng)前題目:OpenFeignAutoConfiguration源碼解析-創(chuàng)新互聯(lián)
轉(zhuǎn)載源于:http://jinyejixie.com/article8/dhosop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、域名注冊(cè)、定制網(wǎng)站、營(yíng)銷型網(wǎng)站建設(shè)、搜索引擎優(yōu)化、網(wǎng)站營(yíng)銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容