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

Spring解析配置類和掃描包路徑的詳細(xì)過程

 更新時(shí)間:2024年12月18日 08:46:48   作者:dougsu  
這篇文章主要介紹了Spring解析配置類和掃描包路徑的詳細(xì)過程,文中通過代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下

目標(biāo)

這是我們使用注解方式啟動(dòng)spring容器的核心代碼

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) applicationContext.getBean("user");
user.printName();

其中配置類MyConfig的代碼是

@ComponentScan(value = "com.mydemo")
public class MyConfig {
}

現(xiàn)在我們的目標(biāo)是搞清楚spring是怎么解析這個(gè)配置類并且掃描該配置類包路徑下的bean?

重要的組件

  • AnnotatedBeanDefinitionReader : spring容器啟動(dòng)的時(shí)候就會(huì)創(chuàng)建這個(gè)讀取器,主要是將類以BeanDefinition的方式保存到bean工廠(DefaultListableBeanFactory)
    在創(chuàng)建這個(gè)讀取器的時(shí)候,spring會(huì)默認(rèn)添加一個(gè)ConfigurationClassPostProcessor的BeanDefinition,這個(gè)就是在解析配置類時(shí)的主要對(duì)象,在AnnotationConfigUtils類的registerAnnotationConfigProcessors中實(shí)現(xiàn)
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
  • ClassPathBeanDefinitionScanner : 路徑掃描器,在spring啟動(dòng)的時(shí)候就會(huì)創(chuàng)建,主要功能就是對(duì)類路徑進(jìn)行掃描,內(nèi)含一些掃描規(guī)則,例如在創(chuàng)建時(shí)候就會(huì)內(nèi)置一個(gè)Component注解的過濾器
protected void registerDefaultFilters() {
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ...
}

加載配置類

我們的配置類是由AnnotatedBeanDefinitionReader類的doRegisterBean方法,轉(zhuǎn)成BeanDefinition存到bean工廠的beanDefinitionMap中,基于ASM獲取一個(gè)類信息轉(zhuǎn)成BeanDefinition。
轉(zhuǎn)成的核心代碼

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);

得到配置類對(duì)象的AnnotatedGenericBeanDefinition后,雖然還沒有加載類,但是已經(jīng)獲取到了類的注解信息。
雖然都是帶有BeanDefinition,但是保存到bean工廠的BeanDefinition和這個(gè)是不一樣的,這個(gè)AnnotatedGenericBeanDefinition主要是一些注解信息,并沒有類似于BeanDefinition的屬性,如是否懶加載,作用域,是否依賴等。

解析AnnotatedGenericBeanDefinition注解信息的主要代碼,主要就是讀取Lazy、Primary 、DependsOn、Description設(shè)置成屬性值

AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
if (lazy != null) {
	abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
	lazy = attributesFor(abd.getMetadata(), Lazy.class);
	if (lazy != null) {
		abd.setLazyInit(lazy.getBoolean("value"));
	}
}

if (metadata.isAnnotated(Primary.class.getName())) {
	abd.setPrimary(true);
}
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
	abd.setDependsOn(dependsOn.getStringArray("value"));
}

AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
	abd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
	abd.setDescription(description.getString("value"));
}

解析AnnotatedGenericBeanDefinition后轉(zhuǎn)成BeanDefinitionHolder才是我們要保存到bean工廠的BeanDefinition

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

如果配置類不是代理模式,就直接保存BeanDefinition到bean工廠中了,
如果是代理模式,就創(chuàng)建一個(gè)新的RootBeanDefinition保存到bean工廠中,主要實(shí)現(xiàn)的代碼在ScopedProxyUtils類createScopedProxy方法中

啟動(dòng)解析組件

spring在啟動(dòng)配置類掃描的任務(wù)時(shí),是以啟動(dòng)一個(gè)BeanDefinitionRegistryPostProcessor的方式調(diào)用掃描類執(zhí)行的,屬于一種組件化啟動(dòng)任務(wù)類的方式

for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
	...
	postProcessor.postProcessBeanDefinitionRegistry(registry);
	...
}

這個(gè)組件的實(shí)現(xiàn)類是ConfigurationClassPostProcessor,所以所有的掃描代碼都在該類的postProcessBeanDefinitionRegistry方法下

定位配置類

在bean工廠的beanDefinitionMap中遍歷每個(gè)元素來定位符合配置類的bd,規(guī)則校驗(yàn)在ConfigurationClassUtils類checkConfigurationClassCandidate方法中:

  1. 主要是確定該bd是AnnotatedBeanDefinition類型,
  2. 如果beanDef不是AnnotatedBeanDefinition的實(shí)例,則進(jìn)一步檢查它是否是AbstractBeanDefinition的實(shí)例并且已經(jīng)有了對(duì)應(yīng)的Class對(duì)象。如果是的話,接著會(huì)檢查這個(gè)Class是否實(shí)現(xiàn)了某些特定接口(如BeanFactoryPostProcessor, BeanPostProcessor, AopInfrastructureBean, 或者EventListenerFactory)。如果確實(shí)實(shí)現(xiàn)了這些接口中的一個(gè)或多個(gè),函數(shù)將返回false,表示不需要繼續(xù)解析。否則,它將通過AnnotationMetadata.introspect(beanClass)方法來獲取該類的注解元數(shù)據(jù)。
  3. 如果以上兩種情況都不滿足,代碼將嘗試通過MetadataReader從類路徑中讀取指定類名(className)的元數(shù)據(jù)。這通常涉及到加載類文件并從中提取信息。如果在這個(gè)過程中發(fā)生IO異常(例如找不到類文件),則記錄錯(cuò)誤信息并返回false。

解析配置類

解析的操作是ConfigurationClassParser來完成的,所有解析的相關(guān)邏輯都在該類的processConfigurationClass方法中,主要負(fù)責(zé)解析和注冊(cè)配置類中的各種注解:
處理@PropertySource @ComponentScan @Import @ImportResour @Bean注解,這里值分析 @ComponentScan注解,因?yàn)橐呀?jīng)獲取到了類的元信息,所以就可以獲取@ComponentScan配置的路徑,進(jìn)而進(jìn)行路徑掃描,掃描是交由ComponentScanAnnotationParser組件執(zhí)行的,由ComponentScanAnnotationParser組件發(fā)起最終在ClassPathBeanDefinitionScanner類型的doScan來實(shí)現(xiàn)

掃描過程

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

通過調(diào)用findCandidateComponents方法,根據(jù)提供的基礎(chǔ)包名(basePackage)來查找該包及其子包下的所有符合組件掃描條件的類,并將它們作為候選組件返回。每個(gè)候選組件都是一個(gè)BeanDefinition對(duì)象,表示潛在的Spring bean:

  • 構(gòu)建搜索路徑:
    構(gòu)建一個(gè)資源模式路徑,用于指示ResourcePatternResolver在哪里查找資源。這個(gè)路徑包括了類路徑前綴、基礎(chǔ)包名以及資源模式(例如/**/*.class),以便于匹配所有的類文件。
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
  • 獲取資源
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

通過getResourcePatternResolver()獲取資源解析器實(shí)例,并調(diào)用其getResources方法來獲取與給定模式匹配的所有資源。這里的資源是指符合路徑模式的類文件。

  • 初步篩選
    遍歷每個(gè)資源,使用MetadataReaderFactory為每個(gè)資源創(chuàng)建一個(gè)MetadataReader實(shí)例,它能夠讀取類的元數(shù)據(jù)而無需加載該類到JVM中。
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);

首先使用isCandidateComponent(metadataReader)方法初步判斷資源是否可能是一個(gè)候選組件:

AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
  1. 類必須是獨(dú)立的(非內(nèi)部類)。
  2. 同時(shí),類必須是具體的(非接口或非抽象類),或者如果是抽象類的話,它必須包含至少一個(gè)用 @Lookup 注解標(biāo)記的方法。
  • 確定是否創(chuàng)建為BeanDefinition
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

對(duì)于每個(gè)候選的BeanDefinition,使用scopeMetadataResolver解析其作用域(scope)信息,同時(shí)為Bean生成或獲取一個(gè)唯一的beanName

if (candidate instanceof AbstractBeanDefinition) {
    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}

如果候選Bean是一個(gè)AbstractBeanDefinition類型的實(shí)例,則調(diào)用postProcessBeanDefinition方法進(jìn)行額外的后處理,比如應(yīng)用默認(rèn)值和自動(dòng)裝配規(guī)則

if (candidate instanceof AnnotatedBeanDefinition) {
	AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}

如果候選Bean是AnnotatedBeanDefinition類型,那么將處理常見的注解,如@Lazy, @Primary, @DependsOn, @Role, 和 @Description等

if (checkCandidate(beanName, candidate)) {
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
	definitionHolder =
			AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	beanDefinitions.add(definitionHolder);
	registerBeanDefinition(definitionHolder, this.registry);
}

檢查當(dāng)前候選Bean是否可以被注冊(cè)到容器中,如果可以,繼續(xù)執(zhí)行以下操作:
創(chuàng)建一個(gè)BeanDefinitionHolder對(duì)象,該對(duì)象持有Bean定義、Bean名稱以及其他元數(shù)據(jù),
如果需要使用applyScopedProxyMode根據(jù)作用域代理模式來創(chuàng)建作用域代理,
將處理后的BeanDefinitionHolder添加到beanDefinitions列表,并注冊(cè)到registry中。

在checkCandidate中還有一個(gè)方法

protected boolean isCompatible(BeanDefinition newDef, BeanDefinition existingDef) {
	return (!(existingDef instanceof ScannedGenericBeanDefinition) ||  // explicitly registered overriding bean
			(newDef.getSource() != null && newDef.getSource().equals(existingDef.getSource())) ||  // scanned same file twice
			newDef.equals(existingDef));  // scanned equivalent class twice
}

檢查新的Bean定義是否與已存在的Bean定義兼容,避免重復(fù)掃描同一個(gè)文件或者類而引起的沖突。

總結(jié)

  1. 配置類加載:使用AnnotatedBeanDefinitionReader將配置類轉(zhuǎn)換為BeanDefinition,并通過ASM庫獲取類信息。
  2. 啟動(dòng)解析組件:通過實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor接口的ConfigurationClassPostProcessor組件來啟動(dòng)配置類的解析任務(wù)。
  3. 定位與解析配置類:遍歷bean工廠中的所有BeanDefinition以定位配置類,并使用ConfigurationClassParser處理配置類上的各種注解,如@ComponentScan。
  4. 組件掃描:ClassPathBeanDefinitionScanner根據(jù)指定的基礎(chǔ)包名查找符合組件掃描條件的類,進(jìn)行初步篩選后創(chuàng)建BeanDefinition對(duì)象,最終注冊(cè)到Spring容器中。

以上就是Spring解析配置類和掃描包路徑的詳細(xì)過程的詳細(xì)內(nèi)容,更多關(guān)于Spring解析配置類和掃描包路徑的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringCloud?Stream?整合RabbitMQ的基本步驟

    SpringCloud?Stream?整合RabbitMQ的基本步驟

    這篇文章主要介紹了SpringCloud?Stream?整合RabbitMQ的基本步驟,從項(xiàng)目介紹到生產(chǎn)者結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • Java的基礎(chǔ)語法學(xué)習(xí)筆記

    Java的基礎(chǔ)語法學(xué)習(xí)筆記

    這里為大家整理了Java的基礎(chǔ)語法學(xué)習(xí)筆記,包括關(guān)鍵詞、運(yùn)算符與基本的流程控制語句寫法等,需要的朋友可以參考下
    2016-05-05
  • SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫連接池和監(jiān)控

    SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫連接池和監(jiān)控

    Druid是Java語言中使用的比較多的數(shù)據(jù)庫連接池。Druid還提供了強(qiáng)大的監(jiān)控和擴(kuò)展功能。面將介紹SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫連接池和監(jiān)控功能,感興趣的可以了解一下
    2021-08-08
  • Netty分布式server啟動(dòng)流程N(yùn)io創(chuàng)建源碼分析

    Netty分布式server啟動(dòng)流程N(yùn)io創(chuàng)建源碼分析

    這篇文章主要介紹了Netty分布式server啟動(dòng)流程N(yùn)io創(chuàng)建源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Java設(shè)計(jì)模式單例模式(Singleton)用法解析

    Java設(shè)計(jì)模式單例模式(Singleton)用法解析

    這篇文章主要介紹了Java設(shè)計(jì)模式單例模式(Singleton)用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過程解析

    Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Spring MVC4.1服務(wù)器端推送實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • mybatis-plus返回map自動(dòng)轉(zhuǎn)駝峰配置操作

    mybatis-plus返回map自動(dòng)轉(zhuǎn)駝峰配置操作

    這篇文章主要介紹了mybatis-plus返回map自動(dòng)轉(zhuǎn)駝峰配置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • Spring RestTemplate使用方法示例總結(jié)

    Spring RestTemplate使用方法示例總結(jié)

    這篇文章主要介紹了Spring RestTemplate使用方法示例總結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-04-04
  • 使用Spring RestTemplate 詳解實(shí)踐使用及拓展增強(qiáng)

    使用Spring RestTemplate 詳解實(shí)踐使用及拓展增強(qiáng)

    這篇文章主要介紹了使用Spring RestTemplate 詳解實(shí)踐使用及拓展增強(qiáng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.Transformer異常

    Caused by: java.lang.ClassNotFoundException: org.apache.comm

    這篇文章主要介紹了Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type異常,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論

财经| 乐东| 左权县| 台南县| 峨边| 广灵县| 崇左市| 内江市| 淮北市| 左权县| 肇源县| 青冈县| 石台县| 阳西县| 资源县| 凉城县| 台湾省| 甘孜| 林周县| 乌拉特前旗| 鹿邑县| 大英县| 伊金霍洛旗| 铜川市| 馆陶县| 滨海县| 乌审旗| 高雄县| 将乐县| 紫阳县| 马尔康县| 肇庆市| 利津县| 栾城县| 新沂市| 岳西县| 汉源县| 资溪县| 张家界市| 娱乐| 蕉岭县|