Spring 源碼詳解IOC (一)

   此篇文章將會從啟動類開始解析Spring是怎么實現(xiàn)自動裝配以及控制反轉(zhuǎn)(基于Spring 5.3.1)
// 啟動類調(diào)用靜態(tài)方法run
SpringApplication.run(CodeGenApplication.class, args);
// 繼續(xù)調(diào)用SpringApplication.createApplicationContext創(chuàng)建ApplicationContext以及調(diào)用
// AnnotatedBeanDefinitionReader使用AnnotationConfigUtils注冊ConfigurationClassPostProcessor  AutowiredAnnotationBeanPostProcessor  CommonAnnotationBeanPostProcessor等后置處理器
// 接下來會調(diào)用AbstractApplicationContext.refresh方法
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

            // Prepare this context for refreshing.
            prepareRefresh();

            // 創(chuàng)建beanFactory以及掃描bean信息然后注冊
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 注冊了ApplicationContextAwareProcessor 以及其它后置處理器
            // 所有實現(xiàn)了Aware接口的對象在實例化時會調(diào)用此AwareProcessor來進行設(shè)置對應(yīng)的屬性
            // 如實現(xiàn)了ApplicationContextAware接口的類會在實例化將此時的ApplicationContext設(shè)置到該類種
            prepareBeanFactory(beanFactory);

            try {
                // 空方法,交給子類實現(xiàn)
                postProcessBeanFactory(beanFactory);

                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // 調(diào)用所有 BeanFactoryPostProcessor触创,此方法會掃描啟動類包下的所有Bean對象坎藐,
                // 并將滿足注入條件的Bean加入到DefaultListableBeanFactory.beanDefinitionMap中此Map為該容器所有bean定義對象集合
                // 調(diào)用所有 BeanFactoryPostProcessor,此方法會掃描啟動類包下的所有Bean對象并將滿足注入條件的Bean(如根注解為@Compontent 包含@Autowire @Repository @Service @Controller等)加入到beanDefinitionMap中也會實例化一部分需要用到的Bean對象
                // 掃描所有第三方依賴的META-INF/spring.factories 使用ConfigurationClassPostProcessor 將spring.factories 中的配置類及其依賴的BeanDefinition注冊并實例化
                invokeBeanFactoryPostProcessors(beanFactory);

                // 從beanFactory獲取所有實現(xiàn)了BeanPostProcessor的BeanDefinition并實例化然后注冊
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();

                // 國際化
                initMessageSource();
                initApplicationEventMulticaster();
                onRefresh();
                registerListeners();

                //  17 完成剩下的普通Bean的實例化哼绑,前面的步驟注冊了大量的BeanDefinition,此時需要進行實例化
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
                contextRefresh.end();
            }
        }
    }

詳解 invokeBeanFactoryPostProcessors(beanFactory)

//以下代碼為PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法內(nèi)代碼片段
//由于SpringApplication.createApplicationContext已經(jīng)注冊了實現(xiàn)BeanDefinitionRegistryPostProcessor的ConfigurationClassPostProcessor處理器
//所以此處currentRegistryProcessors包含ConfigurationClassPostProcessor
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
//獲取優(yōu)先級最高的Bean定義注冊處理器
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 1 執(zhí)行處理器注冊方法岩馍,此處執(zhí)行了ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, 

以下代碼為ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry最終執(zhí)行代碼片段,該方法

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    // 2 獲取此時BeanFactory的所有Bean定義對象的名字
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // 3 如果該Bean定義信息注解類型包含其中一種@Import @ImportResource @Component @ComponetScan,則加入到configCandidates中抖韩,@SpringBootConfiguration根注解為@Component 蛀恩,所以此處存入的為啟動類的Bean定義信息
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    if (configCandidates.isEmpty()) {
        return;
    }

    ConfigurationClassParser 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 {
        StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
        // 4 關(guān)鍵代碼
        // 使用ConfigurationClassParser解析啟動類
        parser.parse(candidates);
        parser.validate();

        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        this.reader.loadBeanDefinitions(configClasses);

    }
    while (!candidates.isEmpty());

}

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 5 此處調(diào)用下方的doProcessConfigurationClass()
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
        //  14 關(guān)鍵切入點 掃描第三方配置以及對應(yīng)包下可以注入的Bean的定義并注冊
        // 第10步已經(jīng)將AutoConfigurationImportSelector實例化并加入deferredImportSelectorHandler.deferredImportSelectors中,實際會調(diào)用AutoConfigurationImportSelector.AutoConfigurationGroup.process()方法
    this.deferredImportSelectorHandler.process();
}

// 6 最后會循環(huán)調(diào)用
protected final SourceClass doProcessConfigurationClass(
            ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
            throws IOException {
    // 7 先處理內(nèi)部類茂浮,沒有則返回
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass, filter);
    }

    //  8 處理@PropertySource注解
    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");
        }
    }

    // 9 處理 @ComponentScan注解
    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) {
            // 10 此處會調(diào)用ComponentScanAnnotationParser掃描該sourceClass的包下所有滿足條件的Bean赦肋,并將其BeanDefinition注冊到容器中
            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());
                }
            }
        }
    }

    // 11 處理 @Import 注解     getImports(sourceClass)方法會解析sourceClass以及其父注解的@Import  啟動類使用@SpringBootApplication父注解為@EnableAutoConfiguration
    //  解析所有@Import內(nèi)引入的Bean块攒,其中包含AutoConfigurationImportSelector并將其實例化加入到ConfigurationClassParser.deferredImportSelectorHandler.deferredImportSelectors中
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

    // 12處理xml配置 @ImportResource 
    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);
        }
    }

    // 13 處理 @Bean 方法 doProcessConfigurationClass結(jié)束后返回到parse方法執(zhí)行第14步(往上看)
    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;
}

以下代碼為AutoConfigurationImportSelector片段

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
            () -> String.format("Only %s implementations are supported, got %s",
                    AutoConfigurationImportSelector.class.getSimpleName(),
                    deferredImportSelector.getClass().getName()));
    //15 獲取配置
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

//16 掃描該工程的所有依賴并解析依賴下的META-INF/spring.factories文件货矮,將文件中的配置類讀取后返回到調(diào)用鏈
// 根據(jù)getCandidateConfigurations返回的配置類的全類名嵌套調(diào)用 第6步ConfigurationClassParser.doProcessConfigurationClass
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
            getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
            + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甜紫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子六剥,更是在濱河造成了極大的恐慌趣避,老刑警劉巖庞呕,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異程帕,居然都是意外死亡住练,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門愁拭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讲逛,“玉大人,你說我怎么就攤上這事岭埠≌祷欤” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵惜论,是天一觀的道長许赃。 經(jīng)常有香客問我,道長馆类,這世上最難降的妖魔是什么混聊? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮乾巧,結(jié)果婚禮上句喜,老公的妹妹穿的比我還像新娘。我一直安慰自己沟于,他們只是感情好咳胃,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著社裆,像睡著了一般拙绊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泳秀,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天标沪,我揣著相機與錄音,去河邊找鬼嗜傅。 笑死金句,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的吕嘀。 我是一名探鬼主播违寞,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼贞瞒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了趁曼?” 一聲冷哼從身側(cè)響起军浆,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挡闰,沒想到半個月后乒融,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡摄悯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年赞季,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奢驯。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡申钩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘪阁,到底是詐尸還是另有隱情撒遣,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布罗洗,位于F島的核電站愉舔,受9級特大地震影響钢猛,放射性物質(zhì)發(fā)生泄漏伙菜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一命迈、第九天 我趴在偏房一處隱蔽的房頂上張望贩绕。 院中可真熱鬧,春花似錦壶愤、人聲如沸淑倾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娇哆。三九已至,卻和暖如春勃救,著一層夾襖步出監(jiān)牢的瞬間碍讨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工蒙秒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留勃黍,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓晕讲,卻偏偏與公主長得像覆获,于是被迫代替她去往敵國和親马澈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容