SpringBoot框架——從SpringBoot看IoC容器初始化流程之方法分析

目錄

一箕别、概觀Spring Boot

二星澳、Spring Boot應(yīng)用初始化

2.1 初始化入口

2.2 SpringApplication的run方法

2.3 方法分析

三沪铭、容器創(chuàng)建與初始化

3.1 creatApplicationContext()方法

3.2 prepareContext(context, environment, listener, applicationArguments, printedBanner)方法

3.3 refreshContext(context)方法

四届巩、總結(jié)

一硅瞧、概觀SpringBoot

隨著分布式、微服務(wù)和云計算時代的到來恕汇,Spring Boot框架的重要性日益突顯腕唧。Spring官方給出的Spring Boot框架的特性:

  • 可以創(chuàng)建獨立的Spring應(yīng)用
  • 直接內(nèi)嵌Tomcat, Jetty 或者 Undertow,不再需要開發(fā)WAR類型文件
  • 提供各式的"starter"依賴瘾英,來簡化項目的創(chuàng)建配置
  • 任何時候都盡可能地自動化配置Spring和第三方庫
  • 提供猶如健康指標(biāo)檢測和外部配置的生產(chǎn)環(huán)境特性
  • 完全沒有代碼生成和不需要xml配置

從官方給出的特性來看枣接,Spring Boot適合做分布式系統(tǒng)和云上應(yīng)用的底層框架,內(nèi)嵌容器不需要再安裝配置容器缺谴,而且提供了很多已經(jīng)配置好的第三方框架和應(yīng)用但惶,并且主張“約定大于配置”,即拿來即用湿蛔,適合使用java config注解配置膀曾,讓開發(fā)人員更關(guān)注與業(yè)務(wù)代碼。所以阳啥,在這么多誘惑下添谊,自己便決定研究一下Spring Boot框架的源碼,從源碼來了解Spring Boot的迷人之處察迟。

二碉钠、Spring Boot應(yīng)用初始化

Spring框架的IoC和AOP是其強大之處,而Spring框架下的應(yīng)用都會使用這兩種技術(shù)卷拘,所以使用該框架的開發(fā)人員都應(yīng)該去了解ioc和aop技術(shù)喊废。而Spring Boot是Spring框架的即用版,因此栗弟,從Spring Boot源碼中學(xué)習(xí)IoC容器的創(chuàng)建初始化以及AOP技術(shù)應(yīng)用的思想污筷,是我認(rèn)為比較便捷的方法。同時乍赫,還可以學(xué)習(xí)到Spring Boot在整合Spring和第三方框架及應(yīng)用的思想和技巧瓣蛀。接下來,我將從Spring Boot應(yīng)用的啟動入口開始分析雷厂,逐步分析啟動流程惋增。

2.1 初始化入口

下面是Spring Boot應(yīng)用最簡單的啟動代碼:

@SpringBootApplication
public class MyApplication {
   public static void main(String[] args){
       SpringApplication.run(MyApplication.class, args );
   }
}

從這段代碼可以知道,應(yīng)用的主方法main調(diào)用了SpringApplication.run()方法改鲫,開啟了應(yīng)用初始化诈皿。那么林束,接下來我們看看它是怎么初始化容器的。

2.2 SpringApplication的run方法

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

2.3 方法分析

  • StopWatch類:計時類稽亏,計算SpringBoot應(yīng)用的啟動時間壶冒。
  • SpringBootExceptionReporter類:是一個回調(diào)接口,用于支持SpringApplication啟動錯誤的自定義報告截歉。
  • configureHeadlessProper()方法:配置Headless模式配置胖腾,該模式下系統(tǒng)缺少顯示設(shè)備、鼠標(biāo)或鍵盤瘪松,而服務(wù)器端往往需要在該模式下工作咸作。
  • getRunListeners(args)方法:獲取SpringApplicationRunListeners類,是SpringApplicationRunListener類的集合宵睦。
  • SpringApplicationRunListener類:SpringApplication的run方法的監(jiān)聽器记罚。
  • DefaultApplicationArguments(args)類:提供訪問運行一個SpringApplication的arguments的訪問入口。
  • prepareEnvironment(listeners,applicationArguments)方法:創(chuàng)建和配置Environment状飞,同時在調(diào)用AbstractEnvironment構(gòu)造函數(shù)時生成PropertySourcesPropertyResolver類毫胜。
  • configureIgnoreBeanInfo(environment)方法:配置系統(tǒng)IgnoreBeanInfo屬性书斜。
  • printBanner(environment)方法:打印啟動的圖形诬辈,返回Banner接口實現(xiàn)類。
  • createApplicationContext()方法:根據(jù)SpringApplication構(gòu)造方法生成的webApplicationType變量創(chuàng)建一個ApplicationContext荐吉,默認(rèn)生成AnnotationConfigApplicationContext焙糟。
  • getSpringFactoriesInstances(SpringBootExceptionRepoter.class, new Class[] {ConfigurableApplicationContext.class }, context)方法:獲取SpringBootExceptionReporter類的集合。
  • prepareContext(context, environment, listener, applicationArguments, printedBanner)方法:設(shè)置Environment样屠,在ApplicationContext中應(yīng)用所有相關(guān)的后處理穿撮,在刷新之前將所有的ApplicationContextInitializers應(yīng)用于上下文,設(shè)置SpringApplicationRunLIstener接口實現(xiàn)類實現(xiàn)多路廣播Spring事件痪欲,添加引導(dǎo)特定的單例(SpringApplicationArguments, Banner)悦穿,創(chuàng)建DefaultListableBeanFactory工廠類,從主類中定位資源并將資源中的bean加載進入ApplicationContext中业踢,向ApplicationContext中添加ApplicationListener接口實現(xiàn)類栗柒。
  • refreshContext(context)方法:調(diào)用AbstractApplicationContext的refresh()方法初始化DefaultListableBeanFactory工廠類。
  • afterRefresh(CongigurableApplicationContext context, ApplicationArguments args)方法:在刷新ApplicationContext之后調(diào)用知举,在SpringAppliation中是方法體為空的函數(shù)瞬沦,故不做任何操作。
  • listeners.started(context)方法:在ApplicationContext已經(jīng)刷新及啟動后雇锡,但CommandLineRunners和ApplicationRunner還沒有啟動時逛钻,調(diào)用該方法向容器中發(fā)布SpringApplicationEvent類或者子類。
  • callRunners(context, applicationArguments)方法:調(diào)用應(yīng)用中ApplicationRunner和CommanLineRunner的實現(xiàn)類锰提,執(zhí)行其run方法曙痘,調(diào)用時機是容器啟動完成之后芳悲,可以用@Order注解來配置Runner的執(zhí)行順序,可以用來讀取配置文件或連接數(shù)據(jù)庫等操作屡江。
  • listeners.running(context)方法:在容器刷新以及所有的Runner被調(diào)用之后芭概,run方法完成執(zhí)行之前調(diào)用該方法。調(diào)用之前得到的SpringApplicationRunListeners類running(context)方法惩嘉。
  • 最后罢洲,向應(yīng)用中返回一個之前獲得到的ApplicationContext。

三文黎、容器創(chuàng)建與初始化

在第二部分的SpringApplication的run()方法中惹苗,我們已經(jīng)大致了解到了Spring Boot應(yīng)用在啟動時做了哪些工作,與容器初始化相關(guān)的方法是createApplciationContext()耸峭、prepareContext(context, environment, listener, applicationArguments, printedBanner)以及refreshContext(context)桩蓉。那這一部分將關(guān)注與Spring IoC容器的創(chuàng)建和初始化相關(guān)的SpringApplication方法。

3.1 creatApplicationContext()方法

根據(jù)SpringApplication構(gòu)造方法生成的webApplicationType變量創(chuàng)建一個ApplicationContext劳闹,默認(rèn)生成AnnotationConfigApplicationContext院究。

(1)時序圖

SpringBoot框架——從SpringBoot看IoC容器初始化流程之方法分析

(2)方法的源碼

    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

(3)方法分析

a. 方法根據(jù)applicationContextClass變量是否為空,決定是否執(zhí)行下面的switch語句本涕,而applicationContextClass變量是由SpringApplicationBuilder類中的contextClass()方法調(diào)用ApplicationContext的setApplicationContextClass()賦值的业汰,默認(rèn)為空;

b. webApplicationType變量是SpringApplication.run()方法調(diào)用構(gòu)造方法時賦值的;

c. switch語句通過反射根據(jù)webApplicationType生成對應(yīng)的容器菩颖,分別是AnnotationConfigServletWebServerApplicationContext样漆、AnnotationConfigReactiveWebServerApplicationContext以及AnnotationConfigApplicationContext的,默認(rèn)生成的是AnnotationConfigApplicationContext晦闰,同時放祟,其構(gòu)造函數(shù)生成AnnotedBeanDefinitonReader和ClassPathBeanDefinitionScanner類;

d. 最后,通過BeanUtils工具類將獲取到的容器類轉(zhuǎn)換成ConfigurableApplicationContext類呻右,返回給應(yīng)用使用跪妥。

3.2 prepareContext(context, environment, listener, applicationArguments, printedBanner)方法

prepareContext方法的作用是,設(shè)置Environment声滥,在ApplicationContext中應(yīng)用相關(guān)的后處理眉撵,在刷新之前將任何的ApplicationContextInitializers應(yīng)用于上下文,設(shè)置SpringApplicationRunLIstener接口實現(xiàn)類實現(xiàn)多路廣播Spring事件醒串,添加引導(dǎo)特定的單例(SpringApplicationArguments, Banner)执桌,創(chuàng)建DefaultListableBeanFactory工廠類,從主類中定位資源并將資源中的bean加載進入ApplicationContext中芜赌,向ApplicationContext中添加ApplicationListener接口實現(xiàn)類仰挣。接下來,我們將從源碼入手缠沈,逐步分析prepareContext()方法所做的工作膘壶。

(1)prepareContext()源碼

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

(2)prepareContext時序圖

SpringBoot框架——從SpringBoot看IoC容器初始化流程之方法分析

(3)方法解析

  • context.setEnvironemnt(environment)方法:為容器設(shè)置環(huán)境错蝴,該Environment類是run()方法中的prepareEnvironment()方法獲取的,默認(rèn)為StandardEnvironment類颓芭。

  • postProcessApplicationContext(context)方法:在ApplicationContext中應(yīng)用相關(guān)的后處理顷锰。
    a. 根據(jù)應(yīng)用中的beanNameGernerator引用是否為空(默認(rèn)為空),決定是否向容器中注入BeanNameGenerator類的實現(xiàn)類亡问;
    b. 根據(jù)resourceLoader引用是否為空(默認(rèn)為DefaultResourceLoader類官紫,在printBanner()階段獲得)以及context的類型,決定配置resource的地方和方式州藕,若context(默認(rèn)為AnnotationConfigApplicationContext)為GenericApplicationContext類或子類束世,則向context中添加    resourceLoader,若context為DefaultResourceLoader類或子類床玻,則向context中添加resourceLoader類的ClassLoader類實例毁涉;
    c. 根據(jù)addConversionService的布爾值(默認(rèn)為true),決定是否向容器中添加ApplicationConversionServivce類實例锈死。

  • applyInitializers(context)方法:在容器刷新之前贫堰,應(yīng)用所有的ApplicationContextInitializer接口實現(xiàn)類。遍歷執(zhí)行g(shù)etInitializers()方法得到的集合的initialize(context)方法初始化context容器待牵,而for方法遍歷的ApplicationContextInitializer集合是應(yīng)用執(zhí)行SpringApplication構(gòu)造函數(shù)時getSpringFactoriesInstances()方法設(shè)置的其屏。

  • listeners.contextPrepared(context)方法:遍歷執(zhí)行run方法里getRunListeners()得到的SpringApplicationRunListeners (SpringApplicationRunListener的集合)的contextPrepared(context)方法。執(zhí)行時機是洲敢,ApplicationContext容器完成創(chuàng)建和準(zhǔn)備之后漫玄,并且只調(diào)用一次茄蚯。容器提供的SpringApplicationRunListener接口實現(xiàn)類是EventPublishingRunListener類压彭,該類構(gòu)造函數(shù)生成的SimpleApplciationEventMulticaster類用來多路廣播發(fā)布SpringApplicationEvent接口的實現(xiàn)類。

  • logStartupInfo(context.getParent()==null)方法:記錄啟動信息渗常,其子類可以覆蓋該方法以添加附加信息壮不。

  • logStartupProfileInfo(context)方法:記錄活動配置文件信息。

  • context.getBeanFactory()方法:調(diào)用之前得到的ApplicationContext容器(默認(rèn)AnnotationConfigApplicationContext)的getBeanFactory()得到一個BeanFactory接口實現(xiàn)類(默認(rèn)DefaultListableBeanFactory類)皱碘,轉(zhuǎn)換為ConfigurableListableBeanFactory類询一。

  • beanFactory.registerSingleton("springApplicationArguments", applicationArguments)方法:往容器中注冊ApplicationArguments接口實現(xiàn)類(默認(rèn)DefaultApplicationArguments類)的單例。

  • beanFactory.registerSingleton("springBootBanner", printedBanner)方法:向容器中添加printBanner(environment)方法返回的Banner類的單例癌椿。

  • ((DefaultListableBeanFactory) beanfactory).setAllowBeanDefinitionOverring(this.allowBeanDefinitionOverriding)方法:設(shè)置BeanDefinitio覆蓋的開關(guān)健蕊。

  • getAllSources()方法:獲取到應(yīng)用中所有的資源,即Bean的資源定位階段踢俄。該資源獲取的地點是缩功,在SpringApplication構(gòu)造方法中產(chǎn)生,將run(MyApplication.class都办,args)中的MyApplication.class轉(zhuǎn)換存儲到LinkedHashSet<>中嫡锌,并返回一個該資源Set的引用primarySources虑稼,在getAllSource()方法中獲取該primarySources。

  • Assert.notEmpty(sources, "Sources must not be empty")方法:斷言势木,判斷獲取到的sources是否為空蛛倦,為空則停止應(yīng)用并返回錯誤信息。

  • load(context, sources.toArray(new Object[0]))方法:從上面獲取的資源中加載bean進入ApplicationContext中啦桌,即bean的加載階段溯壶。加載流程如下:
    a.創(chuàng)建了BeanDefinitionLoader類,將傳入的context容器類型轉(zhuǎn)換為BeanDefinitionRegistry(該轉(zhuǎn)換局部生效);
    b.創(chuàng)建了AnnotedBeanDefinitionReader類甫男、AnnotationScopeMetadataResolver類茸塞、AnnotationBeanNameGenerator類以及ConditionEvaluator類;
    c.創(chuàng)建XmlBeanDefinitionReader類查剖、SimpleSaxErrorHandler類钾虐、XmlValidationModeDetector類以及NameThreadLocal類;
    d. 創(chuàng)建了ClassPathBeanDefintionScannerl類以及GroovyBeanDefintionReader類(該類根據(jù)BeanDefinitionLoader中的isGroovyPresent()返回的布爾值決定是否創(chuàng)建)笋庄;
    e. 配置得到的BeanDefinitionLoader效扫,并調(diào)用其load()方法,開始加載BeanDefinition直砂;
    f. 根據(jù)傳入的資源的類型菌仁,決定加載Bean的方式以及BeanDefinition的類型(默認(rèn)為AnnotatedGenericBeanDefinition類);
    g. 解析含有@Scope注解的BeanDefinition獲得其ScopeMetadata静暂,并將scope添加到獲得的BeanDefinition中济丘;
    h. 解析BeanDefinition獲得beanName;
    i. 解析含有@Lazy注解的BeanDefinition洽蛀,根據(jù)注解屬性值決定是否執(zhí)行setLazyInit()方法摹迷;
    j. 遍歷函數(shù)輸入?yún)?shù)BeanDefinitionCustomizer(該接口是功能性接口)的集合,執(zhí)行其customizer(abd)方法郊供;
    k. 將剛才得到的BeanDefinition和beanName添加到BeanDefinitionHolder中峡碉;
    i. 利用AOP框架創(chuàng)建作用域代理類,根據(jù)剛剛獲得的ScopeMetadata的getScopedProxyMode()方法返回的值驮审,ScopedProxyMode 決定是否生成代理類并決定使用jdk代理生成代理類還是cglib生成代理類鲫寄,并將得到的proxyDefinition代替之前的BeanDefinition添加到BeanDefinitionHolder中;
    m. 向容器中注冊別名aliases和beanName疯淫;

  • listeners.contextLoaded(context)方法:遍歷執(zhí)行之前獲得的SpringApplicationRunListener接口實現(xiàn)類的contextLoaded(context)方法地来。

3.3 refreshContext(context)方法

調(diào)用AbstractApplicationContext的refresh()方法初始化DefaultListableBeanFactory工廠類。

(1)refreshContext(context)源碼

    private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh((ApplicationContext)context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

    }

調(diào)用的AbstractApplicationContext.refresh()源碼:

    public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

(2)refreshContext(context)時序圖

下面時refreshContext(context)實際刷新容器的AbstractApplicationContext.refresh()的時序圖:

SpringBoot框架——從SpringBoot看IoC容器初始化流程之方法分析

(3)方法分析

  • prepareRefresh()方法:為刷新準(zhǔn)備好容器熙掺。
    a. 設(shè)置啟動時間未斑;
    b. 設(shè)置標(biāo)示當(dāng)前context是否活動的標(biāo)簽,active為true,closed為false饿凛,類型為AtomicBoolean;
    c. 在context環(huán)境中初始化placeholder屬性源臭蚁,默認(rèn)不做任何事情载城;
    d. 驗證所有標(biāo)記required的property都是可解析的肌似;
    e. 創(chuàng)建ApplicationEvent的集合,即LinkedHashSet<ApplicationEvent>诉瓦。一旦多路廣播器可用(即prepareContext()階段得到的SimpleApplicationEventMulticaster類)川队,便允許發(fā)布早期的ApplicationEnvent的集合;

  • obtainFreshBeanFactory()方法:告訴子類刷新內(nèi)部的bean工廠
    a. 持有一個工廠類單例(默認(rèn)為DefaultListableBeanFactory)睬澡,并依靠調(diào)用者通過工廠類單例或構(gòu)造方法注冊bean固额;
    b. 將在AbstarctApplicationContext類生成的id和在DefaultListableBeanFatory生成指向自身的弱引用,存儲進入一個由DefaultListableBeanFactory持有的Map中煞聪。

  • prepareBeanFactory(beanFactory)方法:準(zhǔn)備在context中要使用的beanFactory斗躏。配置工廠的標(biāo)準(zhǔn)context特性,比如容器的ClassLoader和后處理器昔脯。
    a. 設(shè)置ClassLoader啄糙;
    b. 設(shè)置BeanExpressionResolver接口實現(xiàn)類(為StandardBeanExpressionResolver類);
    c. 設(shè)置PropertyEditorRegistrar接口實現(xiàn)類(為ResourceEditorRegistrar類);
    e. 將ApplicationContextAwareProcessort添加進入應(yīng)用獲得的工廠類實例持有的CopyOnWriteArrayList的一個引用中云稚;
    f. 將EnvironmentAware.class隧饼、EmbeddedValueResolverAware.class、ResourceLoaderAware.class静陈、ApplicationEventPublisherAware.class燕雁、MessageSourceAware.class、MessageSourceAware.class 添加進入應(yīng)用獲得的工廠類實例持有的HashSet的一個引用中鲸拥;
    g. 在普通工廠類中未注冊成為可解析類型的工廠類接口拐格,而MessageSource作為bean被注冊(查找到并自動裝配)。
    h. 將BeanFactory.class崩泡、ResourceLoader.class禁荒、ApplicationEventPublisher.class猬膨、ApplicationContext.class 添加進入應(yīng)用獲得的工廠類實例持有的ConcurrentHashMap<Class<?>, Object>的一引用中角撞;
    i. 注冊為了檢測內(nèi)部bean的早期后處理器,例如ApplicationListeners勃痴;
    j. 將ApplicationListenerDetector的實例谒所、LoadTimeWeaverAwareProcessor的實例添加進入應(yīng)用獲得的工廠類實例持有的CopyOnWriteArrayList<BeanPostProcessor>的一個引用中;
    k. 為持有的工廠類設(shè)置臨時可以匹配類型的ClassLoader,為ContextTypeMatchClassLoader類沛申;
    l. 配置默認(rèn)的環(huán)境單例bean劣领;

  • postProcessBeanFactory(beanFactory)方法:允許容器子類中的bean工廠執(zhí)行后處理。該方法執(zhí)行時機時铁材,在ApplicationContext內(nèi)部bean工廠類標(biāo)準(zhǔn)初始化之后修改它尖淘,這時所有的BeanDifinition已經(jīng)被加載奕锌,但是還沒有被實例化。繼承這個方法允許某些AbstractApplicationContext類的子類中注冊特殊的類似BeanPostProcessors的類村生。默認(rèn)不做任何操作惊暴。

  • invokeBeanFactoryPostProcessors(beanFactory)方法:實例化所有已經(jīng)注冊的BeanFactoryPostProcessor的bean(實例化為單例),并遵循已給出的明確order值調(diào)用它趁桃,bean實例化與依賴注入開始階段辽话。
    a. 實際調(diào)用了PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessor(beanfactory, getBeanFactoryPostProcessor)方法,在這個方法里完成了實例化和調(diào)用任務(wù)卫病;
    c. 不實例化FactoryBean油啤,并保留所有常規(guī)bean,不初始化它們蟀苛,讓之后的bean工廠后處理器應(yīng)用他們益咬;
    d. 區(qū)分開實現(xiàn)了PriorityOrdered類的BeanDefinitionRegistryPostProcessor類,將其實例化并裝入一個ArrayList的容器中帜平;
    e. 將剛剛獲得的BeanDefinitionRegistryPostProcessor的集合根據(jù)order的值排序础废;
    f. 遍歷調(diào)用得到的BeanDefinitionRegistryPostProcessor類bean的postProcessBeanDefinitionBeanDefinitionRegistry(registry)方法;
    g. 清除BeanDefinitionRegistryPostProcessor的ArrayList集合里的bean罕模;
    h. 區(qū)分開實現(xiàn)了Ordered類的BeanDefinitionRegistryPostProcessor類评腺,將其實例化并裝入一個ArrayList的容器中,并重復(fù)執(zhí)行一次e到g的步驟淑掌;
    i. 最后蒿讥,調(diào)用所有其他的BeanDefinitionRegistryPostProcessor,直到不再出現(xiàn)其他的BeanDefinitionRegistryPostProcessor抛腕,也重復(fù)執(zhí)行一次e到g的步驟芋绸;
    j. 調(diào)用到目前為止的已處理的所有處理器的postProcessBeanFactory回調(diào);
    g. 調(diào)用context實例注冊的factory的processor担敌;
    i. 清空緩存的合并的Bean定義摔敛,因為后處理器可能會修改原始的元數(shù)據(jù),例如全封,替換值中的占位符马昙。

  • registerBeanPostProcessors(beanFactory)方法:注冊攔截bean創(chuàng)建的bean處理器。
    a. 向context持有的bean factory注冊BeanPostProcessorChecker刹悴,該檢查器其用來記錄在BeanPostProcessor實例化期間創(chuàng)建bean時的info信息行楞,即當(dāng)所有BeanPostProcessor都不對bean進行后處理操作時記錄。
    b. 將所有的BeanPostProcessor按照實現(xiàn)PriorityOrdered土匀、Oedered和剩下的區(qū)別子房,分別添加進三個ArrayList容器;
    c. 對剛剛獲得ArrayList分別根據(jù)order值進行排序,最后整合后注冊進入bean factory中证杭;
    d. 重新注冊后處理器田度,用于檢測像ApplicationListener的內(nèi)部類,將其移動到后處理器鏈的末端解愤,用于提取代理等作用每币;

  • initMessageSource()方法:為context初始化message源。從容器中獲得MessageSource的bean琢歇,若沒有定義兰怠,則會調(diào)用父類的MessageSource(無值,并會打印出沒有message source的日志信息)李茫。此例為單例揭保。

  • initApplicationEventMulticaster()方法:實例化應(yīng)用事件多路廣播器ApplicationEventMulticaster。從容器中獲取ApplicationEventMulticaster的bean魄宏,若沒有注冊秸侣,則注入SimpleApplicationEventMulticaster類的bean。

  • onRefresh()方法:該方法使模板方法宠互,在特定的context子類中初始化其他特殊的bean味榛。
    a. 在GenericWebApplicationContext、StaticWebApplicationContext中予跌,該方法初始化了context的ThemeSource搏色;
    b. 在ServletWebServerApplicationContext、ReactiveWebServerApplicationContext中券册,該方法初始化類context的TnemeSource并創(chuàng)建了一個ServerManager频轿。

  • registerListeners()方法: 檢測監(jiān)聽器并注冊它們。向剛才獲得的ApplicationEventMulticaster中注冊實現(xiàn)了ApplicationListener的監(jiān)聽器bean烁焙,但不影響那一些可以不用生成bean就可以加載的監(jiān)聽器航邢。

  • finishBeanFactoryInitialization(beanFactory)方法:實例化所有剩下的單例bean(沒有執(zhí)行l(wèi)azy初始化)。
    a. 初始化context的類型轉(zhuǎn)換服務(wù)骄蝇;
    b. 如果之前沒有任何注冊的bean后處理器(比如膳殷,一個PropertyPlaceholderConfigurer的bean),則注冊一個默認(rèn)的內(nèi)嵌的值解析器九火,在這一觀點下赚窃,優(yōu)先解析注解屬性值;
    c. 盡早初始化LoadTimeWeaverAwarebean吃既,以便盡早注冊它們的轉(zhuǎn)換器考榨;
    d. 停止使用臨時的類型匹配類加載器;
    e. 允許緩存所有的bean定義元數(shù)據(jù)鹦倚,不需要進一步修改了;
    f. 實例化所有剩下的單例(沒有Lazy初始化的bean)冀惭;

  • finishRefresh()方法:發(fā)布相應(yīng)的事件震叙。
    a. 清除context級別的資源緩存(例如掀鹅,掃描得來的ASM元數(shù)據(jù));
    b. 為context初始化生命周期處理器媒楼;
    c. 首先向生命周期處理器傳播刷新乐尊;
    d. 發(fā)布最后的事件(ContextRreshedEvent);
    e. 若激活的話划址,便參與LiveBeansView類的mbean扔嵌,。

  • destroyBeans()方法:摧毀已經(jīng)創(chuàng)建的bean夺颤,避免懸空資源痢缎。

  • cancelRefresh(ex)方法:取消刷新,設(shè)置active變量為false世澜。

  • resetCommonCaches()方法:重置Spring中的內(nèi)省緩存独旷,因為應(yīng)用可能不再需要單例bean的元數(shù)據(jù)了。
    a. 清除聲明方法緩存寥裂,清除聲明變量緩存(ConcurrentReferenceHashMap數(shù)據(jù)類型)嵌洼;
    b. 清除注解相關(guān)緩存;
    c. 清除可解析類型相關(guān)緩存封恰;
    d. 清除相關(guān)類加載器緩存(ConcurrentHashMap)麻养。

四、總結(jié)

到此為止诺舔,分析了Spring Boot應(yīng)用啟動過程run方法中回溺,自己比較感興趣的三個方法,再以此延伸又接觸到了更多的相關(guān)類和相關(guān)方法混萝,重新復(fù)習(xí)了許多java知識(繼承遗遵、重載、重寫逸嘀、接口车要、反射以及設(shè)計模式)。雖然自己還沒有能力寫出這么優(yōu)美的框架崭倘,但是從分析源碼中自己學(xué)習(xí)到了不少的框架設(shè)計知識翼岁,接觸到比較底層的設(shè)計思想。不盡人意的是司光,有些方法和重要類的分析由于自己能力有限琅坡,解釋得還有些模糊,決定在接下來的文章里將這些遺憾彌補回來残家。歡迎指正榆俺!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茴晋,更是在濱河造成了極大的恐慌陪捷,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诺擅,死亡現(xiàn)場離奇詭異市袖,居然都是意外死亡,警方通過查閱死者的電腦和手機烁涌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門苍碟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撮执,你說我怎么就攤上這事微峰。” “怎么了二打?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵县忌,是天一觀的道長。 經(jīng)常有香客問我继效,道長症杏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任瑞信,我火速辦了婚禮厉颤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凡简。我一直安慰自己逼友,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布秤涩。 她就那樣靜靜地躺著帜乞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筐眷。 梳的紋絲不亂的頭發(fā)上黎烈,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天,我揣著相機與錄音匀谣,去河邊找鬼照棋。 笑死,一個胖子當(dāng)著我的面吹牛武翎,可吹牛的內(nèi)容都是我干的烈炭。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼宝恶,長吁一口氣:“原來是場噩夢啊……” “哼符隙!你這毒婦竟也來了趴捅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤膏执,失蹤者是張志新(化名)和其女友劉穎驻售,沒想到半個月后露久,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體更米,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年毫痕,在試婚紗的時候發(fā)現(xiàn)自己被綠了征峦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡消请,死狀恐怖栏笆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臊泰,我是刑警寧澤蛉加,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站缸逃,受9級特大地震影響针饥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜需频,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一丁眼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昭殉,春花似錦苞七、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乾蓬,卻和暖如春惠啄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巢块。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工礁阁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人族奢。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓姥闭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親越走。 傳聞我的和親對象是個殘疾皇子棚品,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,982評論 2 361