最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringCloud @FeignClient注入Spring容器原理分析

 更新時(shí)間:2024年12月31日 10:59:09   作者:systemup_v1  
本文詳細(xì)分析了Spring Boot中@FeignClient注解的掃描和注入過(guò)程,重點(diǎn)探討了@EnableFeignClients注解的工作原理,通過(guò)源碼分析,揭示了@EnableFeignClients如何通過(guò)@Import注解和FeignClientsRegistrar類(lèi)實(shí)現(xiàn)bean定義的加載

前言

本文分析@FeignClient注解如何別掃描并注入到spring容器中,重點(diǎn)分析 @EnableFeignClients工作原理。由于通過(guò)源碼分析涉及內(nèi)容比較多建議根據(jù)文章中流程debug調(diào)試進(jìn)行學(xué)習(xí)。

文章涉及 容器刷新模板方法,ConfigurationClassPostProcessor(bean工廠后置處理器),@Import注解等工作原理分析

@EnableFeignClients分析

在分析前先提出幾個(gè)問(wèn)題:

  • @EnableFeignClients通過(guò)什么原理可以把自己加到spring啟動(dòng)的生命周期中完成feign的bean掃描?
  • Sprintboot run方法如何能掃描 bean definition并放入spring容器中的?
  • Springboot啟動(dòng)階段設(shè)置了哪些BeanFactoryPostProcessor到容器中?

本文在分析的過(guò)程中會(huì)將上述問(wèn)題逐一講解。在@EnableFeignClients注解中可以看到該注解主要功能:

  • 掃描聲@FeignClient 注解聲明的類(lèi)
  • @FeignClient注解的類(lèi)注入后可通過(guò)@Autowire @Component方式進(jìn)行使用。類(lèi)似@Configuration。

真正實(shí)現(xiàn)這些功能其實(shí)通過(guò)@Import注解+FeignClientsRegistrar類(lèi)實(shí)現(xiàn)。

@Import 注解在spring啟動(dòng)生命周期中通過(guò)組合 ImportSelector實(shí)現(xiàn)類(lèi)或者 ImportBeanDefinitionRegistrar實(shí)現(xiàn)類(lèi)完成bean definition 加載

@EnableFeignClients就是用過(guò)這種機(jī)制完成@FeignClient的掃描

在springboot中@Import 注解加載bean definition是通過(guò)Spring的后置處理器 BeanFactoryPostProcessor完成。

源碼調(diào)用分析

下面結(jié)合Springboot整體啟動(dòng)的流程分析下@EnableFeignClients如何被加載的,主要分析關(guān)鍵邏輯具體細(xì)節(jié)不在此處展開(kāi)。

  1. 首先SpringApplication run 方法啟動(dòng)
  2. 執(zhí)行refresh方法 該方法為 AbstractApplicationContext 模板方法
  3. 執(zhí)行 invokeBeanFactoryPostProcessors方法 該方法會(huì)將實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor類(lèi)的后置處理進(jìn)行實(shí)例化并調(diào)用
  4. 執(zhí)行 ConfigurationClassPostProcessor 后置處理處理@ComponentScan @Import @ImportResources @PropertySource等注解
  5. 調(diào)用FeignClientsRegistrar類(lèi)的解析bean definition方法

接下來(lái)分析AbstractApplicationContext 的refresh方法中invokeBeanFactoryPostProcessors調(diào)用邏輯。

此方法主要實(shí)例化 BeanFactoryPostProcessor并調(diào)用 postProcessBeanFactory方法。

特別提示所有BeanFactoryPostProcessor實(shí)例化一定要在所有bean初始化前。

重點(diǎn)分析invokeBeanFactoryPostProcessors方法及bean后置處理器調(diào)用邏輯

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

方法邏輯比較長(zhǎng)但很好理解下圖中紅色框邏輯完全一樣都是從當(dāng)前bean定義中找到 BeanDefinitionRegistryPostProcessor實(shí)現(xiàn)類(lèi)然篩選出優(yōu)先級(jí)注解類(lèi) PriorityOrdered跟排序注解類(lèi)Ordered并調(diào)用完成所有bean的掃描并注冊(cè)到容器中掃描來(lái)源分為:注解&xml。

完成所有bean定義掃描類(lèi)的后置處理器為 ConfigurationClassPostProcessor

ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry方法開(kāi)始解析bean 定義。

postProcessBeanDefinitionRegistry中核心邏輯是通過(guò)配置類(lèi)解析器進(jìn)行解析,配置類(lèi)一般為Springboot中@SpringbootApplication注解修飾類(lèi)。

此處為Springboot啟動(dòng)時(shí)解析入口 ,通過(guò)配置類(lèi)分析

doProcessConfigurationClass方法開(kāi)始解析各種常用注解如:@Component @Import等

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

本文分析@Import注解調(diào)用邏輯

解析Import注解中value并返回所有類(lèi)

開(kāi)始加載bean定義

loadBeanDefinitionsForConfigurationClass 方法開(kāi)始加載Import注解中配置類(lèi)。

通過(guò)調(diào)用棧信息最終找到執(zhí)行FeignClientRegistrar接口

SpringBoot 注解加載流程邏輯

為了對(duì)Springboot中各個(gè)注解是在Spring生命周期每個(gè)階段時(shí)如何執(zhí)行的可以參考下圖,具體流程可以單步debug進(jìn)行分析

總結(jié)

本文簡(jiǎn)單分析了SpringBoot加載bean definition與FeignClient加載流程,由于細(xì)節(jié)邏輯太多本文不在展開(kāi)分析。

相關(guān)文章

  • @CacheEvict中的allEntries與beforeInvocation的區(qū)別說(shuō)明

    @CacheEvict中的allEntries與beforeInvocation的區(qū)別說(shuō)明

    這篇文章主要介紹了@CacheEvict中的allEntries與beforeInvocation的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • window系統(tǒng)安裝jdk jre的教程圖解

    window系統(tǒng)安裝jdk jre的教程圖解

    java開(kāi)發(fā)少不了安裝jdk,jdk可以同時(shí)安裝多個(gè)版本,只要在項(xiàng)目部署時(shí)注意切換版本選擇,下面小編給大家?guī)?lái)了window系統(tǒng)安裝jdk jre的教程圖解,感興趣的朋友一起看看吧
    2018-08-08
  • JAVA 實(shí)現(xiàn)磁盤(pán)文件加解密操作的示例代碼

    JAVA 實(shí)現(xiàn)磁盤(pán)文件加解密操作的示例代碼

    這篇文章主要介紹了JAVA 實(shí)現(xiàn)磁盤(pán)文件加解密操作的示例代碼,幫助大家利用Java實(shí)現(xiàn)文件的加解密,感興趣的朋友可以了解下
    2020-09-09
  • java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn)

    java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)易飛機(jī)大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 搭建Java開(kāi)發(fā)環(huán)境JDK、IDE的詳細(xì)圖文教程

    搭建Java開(kāi)發(fā)環(huán)境JDK、IDE的詳細(xì)圖文教程

    本文詳細(xì)介紹了如何搭建Java開(kāi)發(fā)環(huán)境,包括下載JDK、配置環(huán)境變量、安裝IDE以及編寫(xiě)和運(yùn)行第一個(gè)Java程序,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • Java利用poi實(shí)現(xiàn)word表格轉(zhuǎn)excel

    Java利用poi實(shí)現(xiàn)word表格轉(zhuǎn)excel

    這篇文章主要為大家詳細(xì)介紹了Java如何利用poi實(shí)現(xiàn)word表格轉(zhuǎn)excel,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-03-03
  • Java maven詳細(xì)介紹

    Java maven詳細(xì)介紹

    今天給大家復(fù)習(xí)一下Java基礎(chǔ)知識(shí),簡(jiǎn)單介紹Maven,文中有非常詳細(xì)的解釋,對(duì)Java初學(xué)者很有幫助喲,需要的朋友可以參考下,希望能夠給你帶來(lái)幫助
    2021-09-09
  • SpringBoot項(xiàng)目速度提升之延遲初始化(Lazy Initialization)詳解

    SpringBoot項(xiàng)目速度提升之延遲初始化(Lazy Initialization)詳解

    延遲初始化(Lazy?Initialization)是一種在需要時(shí)才創(chuàng)建或加載對(duì)象的策略,以減少啟動(dòng)時(shí)間和資源消耗,本文就來(lái)講講延遲初始化的具體使用吧
    2023-05-05
  • HttpClient 請(qǐng)求 URL字符集轉(zhuǎn)碼問(wèn)題

    HttpClient 請(qǐng)求 URL字符集轉(zhuǎn)碼問(wèn)題

    這篇文章主要介紹了HttpClient 請(qǐng)求 URL字符集轉(zhuǎn)碼問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Java實(shí)現(xiàn)根據(jù)地址智能識(shí)別省市區(qū)縣

    Java實(shí)現(xiàn)根據(jù)地址智能識(shí)別省市區(qū)縣

    這篇文章主要為大家詳細(xì)介紹了如何編寫(xiě)一個(gè)Java工具類(lèi),可以根據(jù)身份證地址或用戶(hù)輸入的地址,智能識(shí)別并提取出詳細(xì)的省市區(qū)縣信息,感興趣的小伙伴可以了解下
    2025-03-03

最新評(píng)論

扎赉特旗| 南乐县| 乌恰县| 宜阳县| 宜昌市| 邛崃市| 婺源县| 全南县| 双江| 武胜县| 来凤县| 朝阳市| 新源县| 濉溪县| 旬阳县| 东安县| 怀柔区| 西林县| 镇远县| 德清县| 福州市| 金阳县| 报价| 宁化县| 塔河县| 文登市| 徐闻县| 荣昌县| 保山市| 奉新县| 河南省| 若尔盖县| 两当县| 汪清县| 岚皋县| 岳阳县| 德清县| 民和| 祥云县| 武安市| 旬邑县|