渭南公司做網(wǎng)站蘇州seo關(guān)鍵詞優(yōu)化價(jià)格
tips: ConfigurationClassParser 是 Springframework 中的重要類(lèi)。
本章主要是源碼理解,有難度和深度,也枯燥乏味,可以根據(jù)實(shí)際情況選擇閱讀。
位置:org.springframework.context.annotation.ConfigurationClassParser
ConfigurationClassParser 它是解密 configuration 的關(guān)鍵。理解 ConfigurationClassParser 對(duì)理解整個(gè) Spring 框架至關(guān)重要。
一、作用是什么
ConfigurationClassParser
是一個(gè)非常重要的類(lèi),它主要用于解析帶有@Configuration
注解的類(lèi)。
@Configuration
注解表明該類(lèi)用作配置類(lèi),其中可以定義bean和Spring容器應(yīng)如何初始化和管理這些bean。
ConfigurationClassParser
的作用可以從以下幾個(gè)方面詳細(xì)闡述:
- 解析導(dǎo)入的配置:
@Import
注解允許一個(gè)配置類(lèi)導(dǎo)入另一個(gè)配置類(lèi)。ConfigurationClassParser
解析這些@Import
注解,確保所有導(dǎo)入的配置也被處理和應(yīng)用。 - 處理屬性注入:通過(guò)
@PropertySource
注解,可以指定一些屬性文件,這些屬性文件中的屬性可以被注入到Spring管理的bean中。ConfigurationClassParser
負(fù)責(zé)解析這些注解,并確保屬性文件被加載且其值可用于注入。 - 處理
@Conditional
注解: Spring框架允許在bean的注冊(cè)過(guò)程中使用條件邏輯,@Conditional
注解及其派生注解(例如@ConditionalOnClass
,@ConditionalOnProperty
等)使得只有在滿足特定條件時(shí),才會(huì)進(jìn)行bean的注冊(cè)。ConfigurationClassParser
負(fù)責(zé)解析這些條件注解并應(yīng)用其邏輯。 - processDeferredImportSelectors#processImports 處理擴(kuò)展配置( Starter 能夠被處理的核心分支)
二、觸發(fā)時(shí)機(jī)
SpringBoot 應(yīng)用啟動(dòng)過(guò)程中,通過(guò)后置處理器去觸發(fā) ConfigurationClassPostProcessor。 然后再調(diào)用 ConfigurationClassParser
類(lèi)解析
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {....
}
處理如下:
下面我們將詳細(xì)分析源碼流程。
三、ConfigurationClassPostProcessor
下面是 ConfigurationClassPostProcessor 部分核心代碼。
入口方法 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 。開(kāi)始分析這段代碼。
注意這里有一個(gè) do...while
do {.....}while (!candidates.isEmpty());
它將逐一識(shí)別和解析配置類(lèi),然后將配置類(lèi)中定義的Bean注冊(cè)到Spring容器中。這個(gè)過(guò)程通過(guò)不斷循環(huán)直到?jīng)]有新的配置類(lèi)候選者出現(xiàn)為止,確保了所有相關(guān)的配置都被完整地處理。
// 創(chuàng)建一個(gè)配置類(lèi)解析器,用于解析和處理配置類(lèi)信息
ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);// 初始化一個(gè)集合,用于存儲(chǔ)待處理的配置類(lèi)候選者
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 初始化一個(gè)集合,用于跟蹤已經(jīng)解析過(guò)的配置類(lèi),以避免重復(fù)解析
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());// 循環(huán)處理,直到?jīng)]有新的配置類(lèi)候選者
do {// 解析當(dāng)前候選者中的配置類(lèi)parser.parse(candidates);// 對(duì)解析結(jié)果進(jìn)行驗(yàn)證parser.validate();// 從解析器中獲取已解析的配置類(lèi)集合Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());// 移除已經(jīng)處理過(guò)的,避免重復(fù)處理configClasses.removeAll(alreadyParsed);// 如果讀取器未初始化,創(chuàng)建一個(gè)配置類(lèi)Bean定義讀取器if (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 加載并注冊(cè)配置類(lèi)中定義的Beanthis.reader.loadBeanDefinitions(configClasses);// 將這批配置類(lèi)標(biāo)記為“已解析”alreadyParsed.addAll(configClasses);// 清空候選者集合,為下一輪尋找新候選者做準(zhǔn)備candidates.clear();// 檢查是否有新的Bean定義被注冊(cè)(可能由@Configuration類(lèi)引入)if (registry.getBeanDefinitionCount() > candidateNames.length) {// 重新獲取所有Bean定義的名稱String[] newCandidateNames = registry.getBeanDefinitionNames();// 創(chuàng)建一個(gè)舊候選名稱的集合,用于辨識(shí)新的候選者Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));// 創(chuàng)建一個(gè)集合,用于跟蹤已經(jīng)解析的配置類(lèi)的類(lèi)名Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}// 遍歷新的Bean定義名稱,尋找新的配置類(lèi)候選者for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}// 更新候選名稱列表,以反映新的Bean定義candidateNames = newCandidateNames;}
// 如果還有未處理的候選者,繼續(xù)循環(huán)
} while (!candidates.isEmpty());
特別說(shuō)明,對(duì)于 Bean 的加載和實(shí)例化不在本范圍了,不進(jìn)行講解。感興趣可以閱讀相關(guān)章節(jié)。
上面的這段代碼是 ConfigurationClassPostProcessor 核心。解析來(lái)的重頭戲。ConfigurationClassParser
四、ConfigurationClassParser
從這行代碼開(kāi)始入手parser.parse(candidates);
- parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
- processDeferredImportSelectors(); // 處理前面推遲的ImportSelector,一些二方包的導(dǎo)入類(lèi),將在這個(gè)方法中實(shí)現(xiàn)。 例如,我們配置在 Starter Spring.factories 中的自動(dòng)導(dǎo)入類(lèi),將在這一環(huán)境被加載
parse方法入口
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {// 如果這個(gè)配置類(lèi)應(yīng)該根據(jù)條件注解被跳過(guò),則直接返回不進(jìn)行處理if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}// 嘗試從已處理的配置類(lèi)映射中獲取這個(gè)配置類(lèi)ConfigurationClass existingClass = this.configurationClasses.get(configClass);// 如果找到了已存在的配置類(lèi)if (existingClass != null) {// 如果當(dāng)前處理的是一個(gè)導(dǎo)入的配置類(lèi)if (configClass.isImported()) {// 如果已存在的配置類(lèi)也是導(dǎo)入的,則合并導(dǎo)入來(lái)源if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}// 如果已存在的配置類(lèi)不是導(dǎo)入的,則忽略當(dāng)前導(dǎo)入的配置類(lèi),保留現(xiàn)有的非導(dǎo)入類(lèi)return;}else {// 如果找到顯式的bean定義,可能是意在替換一個(gè)導(dǎo)入的類(lèi)。// 移除舊的配置類(lèi),采用新的配置類(lèi)。this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// 遞歸處理配置類(lèi)及其超類(lèi)層次結(jié)構(gòu)SourceClass sourceClass = asSourceClass(configClass);do {// 處理當(dāng)前配置類(lèi)并更新sourceClass為配置類(lèi)的超類(lèi),準(zhǔn)備下一輪處理sourceClass = doProcessConfigurationClass(configClass, sourceClass);} while (sourceClass != null); // 如果sourceClass為null,表示超類(lèi)已經(jīng)處理完畢// 將處理完的配置類(lèi)放入配置類(lèi)映射中,標(biāo)記為已處理this.configurationClasses.put(configClass, configClass);
}
上面可以理解,解析 MyApplication 類(lèi)所在工程中的類(lèi)。最終的解析由 doProcessConfigurationClass 實(shí)現(xiàn)
processDeferredImportSelectors
負(fù)責(zé)處理那些被延遲的特殊接口,使用它來(lái)按需動(dòng)態(tài)地導(dǎo)入配置。
這些常常依賴于某些條件才被執(zhí)行,所以被延遲處理。
// 定義處理延遲的ImportSelector的方法
private void processDeferredImportSelectors() {// 獲取之前收集的所有延遲處理的ImportSelectorList<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;// 將引用置為null,表示開(kāi)始處理過(guò)程,防止重復(fù)處理this.deferredImportSelectors = null;// 如果沒(méi)有需要處理的延遲ImportSelector,則直接返回if (deferredImports == null) {return;}...... // 遍歷所有的延遲ImportSelectorfor (DeferredImportSelectorHolder deferredImport : deferredImports) {// 獲取與當(dāng)前ImportSelector相關(guān)聯(lián)的配置類(lèi)ConfigurationClass configClass = deferredImport.getConfigurationClass();try {// 調(diào)用ImportSelector的selectImports方法,獲取所有的導(dǎo)入類(lèi)名String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());// 處理這些導(dǎo)入的類(lèi),將它們作為配置類(lèi)進(jìn)行進(jìn)一步的處理processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);}}
}
這個(gè)方法的主要作用將是找出符合條件的 imports 類(lèi)。最終還是由processImports() 處理。
到這里,spring.factories 符合條件的一些類(lèi)將被加載。核心代碼deferredImport.getImportSelector().selectImports(configClass.getMetadata());
到這里我們基本上了解了 Starter 是如何被引入進(jìn)來(lái)的。
真正解析的方法 doProcessConfigurationClass
doProcessConfigurationClass
它遞歸地處理嵌套類(lèi)、處理@PropertySource注解、處理@ComponentScan注解、處理@Import注解、處理@ImportResource注解、處理@Bean方法、處理接口上的默認(rèn)方法,最后處理父類(lèi)
- 處理成員類(lèi):方法首先遞歸地處理配置類(lèi)中定義的任何成員類(lèi)(嵌套類(lèi))。
- 處理
@PropertySource
注解:然后遍歷配置類(lèi)上的所有@PropertySource
注解,這些注解用來(lái)指明屬性文件的位置。如果當(dāng)前的環(huán)境實(shí)現(xiàn)了ConfigurableEnvironment
接口,則處理注解指定的屬性源 - 處理
@ComponentScan
注解:接下來(lái),處理配置類(lèi)上的所有@ComponentScan
注解,這些注解指示Spring掃描特定包下的組件(即帶有@Component
、@Service
等注解的類(lèi)),并注冊(cè)為Spring容器中的Bean。如果有條件注解指示在此階段跳過(guò)處理,則不執(zhí)行掃描。 - 處理
@Import
注解:處理配置類(lèi)上的@Import
注解,這些注解用來(lái)導(dǎo)入其他配置類(lèi)或配置選擇器,允許模塊化地組織配置。 - 處理
@ImportResource
注解:處理配置類(lèi)上的@ImportResource
注解,這些注解用于導(dǎo)入XML配置文件。 - 處理
@Bean
方法:收集配置類(lèi)中所有帶有@Bean
注解的方法的元數(shù)據(jù),并將它們添加到配置類(lèi)對(duì)象中。這些方法定義了應(yīng)該由Spring容器管理的Bean。 - 處理接口上的默認(rèn)方法:如果配置類(lèi)實(shí)現(xiàn)了接口,并在這些接口上定義了默認(rèn)方法,這些方法也會(huì)被處理。
- 處理父類(lèi):最后,如果配置類(lèi)有超類(lèi),那么這個(gè)方法會(huì)檢查超類(lèi)是否也是一個(gè)配置類(lèi),不是Java內(nèi)置類(lèi),并且還沒(méi)有被處理過(guò)。如果滿足條件,則遞歸地處理這個(gè)超類(lèi)。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// 首先,遞歸處理任何成員類(lèi)(嵌套類(lèi))processMemberClasses(configClass, sourceClass);// 處理所有的@PropertySource注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {// 如果環(huán)境實(shí)現(xiàn)了ConfigurableEnvironment接口if (this.environment instanceof ConfigurableEnvironment) {// 處理@PropertySource注解processPropertySource(propertySource);} else {// 如果環(huán)境沒(méi)有實(shí)現(xiàn)ConfigurableEnvironment接口logger.warn("忽略了[" + sourceClass.getMetadata().getClassName() +"]上的@PropertySource注解。原因:環(huán)境必須實(shí)現(xiàn)ConfigurableEnvironment接口");}}// 處理所有的@ComponentScan注解Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// 如果存在@ComponentScan注解,并且當(dāng)前階段不應(yīng)該跳過(guò)if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {// 循環(huán)處理每個(gè)@ComponentScan注解for (AnnotationAttributes componentScan : componentScans) {// 配置類(lèi)上存在@ComponentScan注解 -> 立即執(zhí)行掃描Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// 檢查掃描結(jié)果中的定義集合,如果有進(jìn)一步的配置類(lèi),遞歸解析for (BeanDefinitionHolder holder : scannedBeanDefinitions) {if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());}}}}// 處理所有的@Import注解processImports(configClass, sourceClass, getImports(sourceClass), true);// 處理所有的@ImportResource注解AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);// 如果@ImportResource注解存在if (importResource != null) {// 獲取資源位置String[] resources = importResource.getStringArray("locations");// 獲取資源的閱讀器類(lèi)Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");// 循環(huán)處理每個(gè)資源for (String resource : resources) {// 解析資源位置中的占位符String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);// 把解析后的資源添加到配置類(lèi)configClass.addImportedResource(resolvedResource, readerClass);}}// 處理單獨(dú)的@Bean方法Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);// 循環(huán)處理每個(gè)@Bean方法for (MethodMetadata methodMetadata : beanMethods) {// 添加@Bean方法到配置類(lèi)configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// 處理接口上的默認(rèn)方法processInterfaces(configClass, sourceClass);// 處理父類(lèi),如果存在的話if (sourceClass.getMetadata().hasSuperClass()) {// 獲取父類(lèi)名稱String superclass = sourceClass.getMetadata().getSuperClassName();// 如果父類(lèi)存在,并且父類(lèi)不是java.*開(kāi)頭,并且尚未處理過(guò)if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {// 記錄已知的父類(lèi)this.knownSuperclasses.put(superclass, configClass);// 找到父類(lèi),返回其注解元數(shù)據(jù)并遞歸處理return sourceClass.getSuperClass();}}// 沒(méi)有父類(lèi) -> 處理完成return null;
}
到這里,大體流程我們已經(jīng)清楚。不再繼續(xù)針對(duì)注解的解析進(jìn)行講解,感興趣可以自行下載源碼閱讀理解。
五、本章小結(jié)
本章是對(duì)整 ConfigurationClassParser 進(jìn)行講解,它是 Spring framework 中的最核心類(lèi)。
到這里,Starter 的整個(gè)過(guò)程已經(jīng)分析完成,但是針對(duì)條件裝配,我們將在下一章進(jìn)行講解。
?已同步發(fā)布到公眾號(hào):面湯放鹽?第七節(jié) ConfigurationClassParser 源碼分析 (qq.com)
掘金賬號(hào):第七節(jié) ConfigurationClassParser 源碼分析 - 掘金 (juejin.cn)