spring @bean 自动创建容器对象的原理
一、在applicationContext.refresh方法中會調用invokeBeanFactoryPostProcessors(beanFactory),此方法會調用當前系統容器中所有注冊的BeanDefinitionRegistryPostProcessor的對象,最終會調用ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry。
二、在ConfigurationClassPostProcessor.processConfigBeanDefinitions會循環遍歷當前容器工廠內的類元注解中包含configuration,componen,ComponentScan、?Import?和?ImportResource的對象。
三、接著會調用ConfigurationClassParser.parse會對每個上面篩選出來的對象進行解析,如果有condition注解在類上,則會判斷條件注解是否滿足,不滿足直接過濾,滿足條件的保存到ConfigurationClass列表中。
1.@import注解的類解析在ConfigurationClassParser.doProcessConfigurationClass。
? ? ? 1.1.如果注解中的類是普通類,則會遞歸調用ConfigurationClassParser.processConfigurationClass進行解析,并保存到parser的configurationClasses中。
? ??
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);? ? ? 1.2 如果注解中的類是實現了ImportSelector接口,則會調導入類的selectImports接口獲取具體要導入的類字符串數組,然后通過asSourceClasses轉換成類,再次遞歸調用processImports,然后就循環到1.1步驟。
? ? 注意:這里有個如果類繼承了DeferredImportSelector,則為延遲加載,會放在所有普通導入BEAN的最后進行加載,并可以控制容器內所有DeferredImportSelector的加載順序(@ORDER),這個應用場景主要用于自動配置,@springbootApplication的注解引入的@EnableAutoConfiguration注解,里面引入的那個importSelect就是延遲selector,spring先處理我們的,然后再處理自動裝配的,那些自動配置類上的那些條件就可以根據我們的配置情況來判斷是否應該生效。
// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);Predicate<String> selectorFilter = selector.getExclusionFilter();if (selectorFilter != null) {exclusionFilter = exclusionFilter.or(selectorFilter);}if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);}else {String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}? ?1.3.如果注解中的導入類是ImportBeanDefinitionRegistrar類,則會將此類保存到configurationClass的importBeanDefinitionRegistrars屬性列表中。
Class<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());2.@bean注解的類解析在ConfigurationClassParser.doProcessConfigurationClass。這個相對簡單,就是解析當前解析類中所有包含@bean注解的方法,并保存到configurationClass的beanMethods集合列表中。
// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}動態數據如下:
3.@ComponentScan注解的類解析在ConfigurationClassParser.doProcessConfigurationClass,這個就是根據@COmponentScan配置的包路徑列表逐個使用ComponentScanAnnotationParser.parse方法掃描包路徑下的包,然后將獲取的BeanDefinitionHolder集合列表再次遞歸調用parse(bdCand.getBeanClassName(), holder.getBeanName())進行處理,添加到parser的configurationClasses中。
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 immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (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());}}}}4.@ImportResource注解的類解析在ConfigurationClassParser.doProcessConfigurationClass,這個注解用于導入Spring的配置文件,讓配置文件里面的內容生效;(就是以前寫的springmvc.xml、applicationContext.xml),這個解析也比較簡單,只是將注解locations屬性和BeanDefinitionReader的讀取類作為MAP的鍵值保存到configurationClasse的importedResources列表屬性中。
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);}}四、然后會調用ConfigurationClassBeanDefinitionReader.loadBeanDefinitions 循環解析每個組件對象,將新增的@bean,@import符合條件的注解對象,注入到beanfactory工廠對象中。
if (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses);1.@bean注解的BeanDefinition對象生成實現為ConfigurationClassBeanDefinition,并設置此BEAN的工廠方法為當前@BEAN注解的方法,工廠BEAN名稱為當前對象,最后會調用容器工廠將此BeanDefinition注冊進去保存到beanDefinitionMap,如下圖
下面是配置類的代碼:
package com.tpw.newday.service.people;import com.tpw.newday.bean.PeopleBean; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration;/*** <h3>newday</h3>* <p></p>** @author : lipengyao* @date : 2021-07-01 15:38:01**/ @Configuration @Conditional(BeanFactoryCondition.class) public class MsgConf {@Bean(name = {"msgSpringBean"})@Conditional(SpringCondition.class)public PeopleBean msgSpringBean(){System.out.println(" MsgConf -->msgSpringBean: " );return new PeopleBean("msgSpringBean");}@Bean(name = {"msgOriginFactoryBean"})@Conditional(BeanFactoryCondition.class)public PeopleBean msgOriginFactoryBean(){System.out.println(" MsgConf -->msgOriginFactoryBean: " );return new PeopleBean("msgOriginFactoryBean");} }2.@importResource注解的BeanDefinition對象會先根據RESOURCE的配置文件路徑生成XmlBeanDefinitionReader(register容器工廠作為參數),然后調用
reader.loadBeanDefinitions(resource),將配置中的所有BEAN實例注冊到容器工廠中。
private void loadBeanDefinitionsFromImportedResources(Map<String, Class<? extends BeanDefinitionReader>> importedResources) {Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();importedResources.forEach((resource, readerClass) -> {// Default reader selection necessary?if (BeanDefinitionReader.class == readerClass) {if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {// When clearly asking for Groovy, that's what they'll get...readerClass = GroovyBeanDefinitionReader.class;}else {// Primarily ".xml" files but for any other extension as wellreaderClass = XmlBeanDefinitionReader.class;}}BeanDefinitionReader reader = readerInstanceCache.get(readerClass);if (reader == null) {try {// Instantiate the specified BeanDefinitionReaderreader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);// Delegate the current ResourceLoader to it if possibleif (reader instanceof AbstractBeanDefinitionReader) {AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);abdr.setResourceLoader(this.resourceLoader);abdr.setEnvironment(this.environment);}readerInstanceCache.put(readerClass, reader);}catch (Throwable ex) {throw new IllegalStateException("Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");}}// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocationsreader.loadBeanDefinitions(resource);});}3.@import中實現了ImportBeanDefinitionRegistrar接口,則會把configurationClass的importBeanDefinitionRegistrars屬性列表所有導入BEAN注冊表循環注冊復制到容器工廠注冊表中。這個比較簡單。
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {registrars.forEach((registrar, metadata) ->registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));}五、循環遍歷當前新解析出來的對象,又回到第二步的邏輯,判斷新對象中是否包含configuration,componen,ComponentScan、?Import?和?ImportResource的注解,如果有,則添加到新的candidates中,再次循環,解析,加載數據。
// Parse each @Configuration classConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());do {parser.parse(candidates);parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}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));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());六、前面所有的工作都是注冊
BeanDefinition到容器工廠,具體的BEAN真正實例化創建都是在最后的applicationContext. finishBeanFactoryInitialization中去初始化非延遲的單例BEAN,這個前面文章已經講過,不再復核。總結
以上是生活随笔為你收集整理的spring @bean 自动创建容器对象的原理的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: spring @import注解使用场景
- 下一篇: spring @order控制对象的顺序
