1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【132期】面試再被問到Spring容器IOC初始化過程,就拿這篇文章砸他~

        共 14930字,需瀏覽 30分鐘

         ·

        2021-02-04 00:51

        程序員的成長之路
        互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
        關(guān)注


        閱讀本文大概需要 14?分鐘。

        作者:擁抱心中的夢想
        juejin.im/post/5ab30714f265da237b21fbcc

        一、老規(guī)矩,先比比點幺蛾子

        作為一個經(jīng)常使用 Spring 的后端程序員,小編很早就想徹底弄懂整個 Spring 框架了!但它整體是非常大的,所有繼承圖非常復(fù)雜,加上小編修行尚淺,顯得力不從心。不過,男兒在世當(dāng)立志,今天就先從 Spring IOC 容器的初始化開始說起,即使完成不了對整個 Spring 框架的完全掌握,也不丟人,因為小編動手了,穩(wěn)住,咱能贏!
        下面說一些閱讀前的建議:
        • 1、閱讀源碼分析是非常無聊的,但既然你進(jìn)來了,肯定也是對這個東西進(jìn)行了解,也希望這篇總結(jié)能對你有所啟發(fā)。

        • 2、前方高能,文章可能會非常的長,圖文并茂。

        • 3、閱讀前建議你對相關(guān)設(shè)計模式、軟件設(shè)計 6 大原則有所了解,小編會在行文中進(jìn)行穿插。

        • 4、小編在讀大四,學(xué)識尚淺,喜歡專研,如果你發(fā)現(xiàn)文章觀點有所錯誤或者與你見解有差異,歡迎評論區(qū)指出和交流!

        • 5、建議你邊看文章的時候可以邊在 IDE 中進(jìn)行調(diào)試跟蹤

        • 6、文章所有 UML 圖利用 idea 自動生成,具體生成方法為:選中一個類名,先ctrl+shift+alt+U,再ctrl+alt+B,然后回車即可

        二、文章將圍繞什么來進(jìn)行展開?

        不多,就一行代碼,如下圖:
        這句是 Spring 初始化的代碼,雖然只有一句代碼,但內(nèi)容賊多!

        三、Spring 容器 IOC 有哪些東西組成?

        這樣子,小編先理清下思路,一步一步來:
        • 1、上面那句代碼有個文件叫applicationContext.xml, 這是個資源文件,由于我們的bean都在里邊進(jìn)行配置定義,那 Spring 總得對這個文件進(jìn)行讀取并解析吧!所以 Spring 中有個模塊叫Resource模塊,顧名思義,就是資源嘛!用于對所有資源xml、txt、property等文件資源的抽象。關(guān)于對Resource的更多知識,可以參考下邊兩篇文章:

        談一談我對 Spring Resource 的理解:https://juejin.im/post/5ab0ce60518825611a405106
        Spring 資源文件剖析和策略模式應(yīng)用(李剛):http://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/index.html
        下面先貼一張小編生成的類圖(圖片有點大,不知道會不會不清晰,如果不清晰可以按照上面說的idea生成方法去生成即可)
        可以看到Resource是整個體系的根接口,點進(jìn)源碼可以看到它定義了許多的策略方法,因為它是用了策略模式這種設(shè)計模式,運用的好處就是策略接口/類定義了同一的策略,不同的子類有不同的具體策略實現(xiàn),客戶端調(diào)用時傳入一個具體的實現(xiàn)對象比如UrlResource或者FileSystemResource策略接口/類Resource即可!
        所有策略如下:
        • 2、上面講了 Spring 框架對各種資源的抽象采用了策略模式,那么問題來了,現(xiàn)在表示資源的東西有了,那么是怎么把該資源加載進(jìn)來呢?于是就有了下面的ResourceLoader組件,該組件負(fù)責(zé)對 Spring 資源的加載,資源指的是xml、properties等文件資源,返回一個對應(yīng)類型的Resource對象。。UML 圖如下:

        從上面的 UML 圖可以看出,ResourceLoader組件其實跟Resource組件差不多,都是一個根接口,對應(yīng)有不同的子類實現(xiàn),比如加載來自文件系統(tǒng)的資源,則可以使用FileSystemResourceLoader, 加載來自ServletContext上下文的資源,則可以使用ServletContextResourceLoader。還有最重要的一點,從上圖看出,ApplicationContext,AbstractApplication是實現(xiàn)了ResourceLoader的,這說明什么呢?說明我們的應(yīng)用上下文ApplicationContext擁有加載資源的能力,這也說明了為什么可以通過傳入一個String resource pathClassPathXmlApplicationContext("applicationContext.xml")就能獲得 xml 文件資源的原因了!清晰了嗎?nice!
        • 3、上面兩點講到了,好!既然我們擁有了加載器ResourceLoader,也擁有了對資源的描述Resource, 但是我們在 xml 文件中聲明的標(biāo)簽在 Spring 又是怎么表示的呢?注意這里只是說對bean的定義,而不是說如何將轉(zhuǎn)換為bean對象。我想應(yīng)該不難理解吧!就像你想表示一個學(xué)生Student,那么你在程序中肯定要聲明一個類Student吧!至于學(xué)生數(shù)據(jù)是從excel導(dǎo)入,或者程序運行時new出來,或者從xml中加載進(jìn)來這些都不重要,重要的是你要有一個將現(xiàn)實中的實體表示為程序中的對象的東西,所以也需要在 Spring 中做一個定義!于是就引入一個叫BeanDefinition的組件,UML 圖如下:

        下面講解下 UML 圖:
        首先配置文件中的標(biāo)簽跟我們的BeanDefinition是一一對應(yīng)的,元素標(biāo)簽擁有classscope、lazy-init等配置屬性,BeanDefinition則提供了相應(yīng)的beanClass、scope、lazyInit屬性。
        其中RootBeanDefinition是最常用的實現(xiàn)類,它對應(yīng)一般性的元素標(biāo)簽,GenericBeanDefinition是自2.5以后新加入的bean文件配置屬性定義類,是一站式服務(wù)類。在配置文件中可以定義父和子,父RootBeanDefinition表示,而子ChildBeanDefiniton表示,而沒有父就使用RootBeanDefinition表示。AbstractBeanDefinition對兩者共同的類信息進(jìn)行抽象。?Spring通過BeanDefinition將配置文件中的配置信息轉(zhuǎn)換為容器的內(nèi)部表示,并將這些BeanDefiniton注冊到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的內(nèi)存數(shù)據(jù)庫,主要是以map的形式保存,后續(xù)操作直接從BeanDefinitionRegistry中讀取配置信息。一般情況下,BeanDefinition只在容器啟動時加載并解析,除非容器刷新或重啟,這些信息不會發(fā)生變化,當(dāng)然如果用戶有特殊的需求,也可以通過編程的方式在運行期調(diào)整BeanDefinition的定義。
        • 4、有了加載器ResourceLoader,也擁有了對資源的描述Resource,也有了對bean的定義,我們不禁要問,我們的Resource資源是怎么轉(zhuǎn)成我們的BeanDefinition的呢? 因此就引入了BeanDefinitionReader組件, Reader 嘛!就是一種讀取機制,UML 圖如下:

        從上面可以看出,Spring 對 reader 進(jìn)行了抽象,具體的功能交給其子類去實現(xiàn),不同的實現(xiàn)對應(yīng)不同的類,如PropertiedBeanDefinitionReader,XmlBeanDefinitionReader對應(yīng)從 Property 和 xml 的 Resource 解析成BeanDefinition。
        其實這種讀取數(shù)據(jù)轉(zhuǎn)換成內(nèi)部對象的,不僅僅是 Spring 專有的,比如:Dom4j 解析器SAXReader reader = new SAXReader(); Document doc = reader.read(url.getFile());//url 是一個 URLResource 對象 嚴(yán)格來說,都是 Reader 體系吧,就是將統(tǒng)一資源數(shù)據(jù)對象讀取轉(zhuǎn)換成相應(yīng)內(nèi)部對象。
        • 5、好了!基本上所有組件都快齊全了!對了,還有一個組件,你有了BeanDefinition后,你還必須將它們注冊到工廠中去,所以當(dāng)你使用getBean()方法時工廠才知道返回什么給你。還有一個問題,既然要保存注冊這些bean, 那肯定要有個數(shù)據(jù)結(jié)構(gòu)充當(dāng)容器吧!沒錯,就是一個Map, 下面貼出BeanDefinitionRegistry的一個實現(xiàn),叫SimpleBeanDefinitionRegistry的源碼圖:

        BeanDefinitionRegistry的 UML 圖如下:
        從圖中可以看出,BeanDefinitionRegistry有三個默認(rèn)實現(xiàn),分別是SimpleBeanDefinitionRegistryDefaultListableBeanFactory,GenericApplicationContext, 其中SimpleBeanDefinitionRegistry,DefaultListableBeanFactory都持有一個 Map,也就是說這兩個實現(xiàn)類把保存了 bean。而GenericApplicationContext則持有一個DefaultListableBeanFactory對象引用用于獲取里邊對應(yīng)的 Map。在DefaultListableBeanFactory
        GenericApplicationContext
        • 6、前面說的 5 個點基本上可以看出ApplicationContext上下文基本直接或間接貫穿所有的部分,因此我們一般稱之為容器,除此之外,ApplicationContext還擁有除了bean容器這種角色外,還包括了獲取整個程序運行的環(huán)境參數(shù)等信息(比如 JDK 版本,jre 等),其實這部分 Spring 也做了對應(yīng)的封裝,稱之為Enviroment, 下面就跟著小編的 eclipse, 一起 debug 下容器的初始化工程吧!

        四、實踐是檢驗真理的唯一標(biāo)準(zhǔn)

        學(xué)生類Student.java如下:
        package com.wokao666;

        public class Student {

        private int id;
        private String name;
        private int age;

        public int getId() {
        return id;
        }

        public void setId(int id) {
        this.id = id;
        }

        public String getName() {
        return name;
        }

        public void setName(String name) {
        this.name = name;
        }

        public int getAge() {
        return age;
        }

        public void setAge(int age) {
        this.age = age;
        }

        public Student(int id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        }

        public Student() {
        super();
        }

        @Override
        public String toString() {
        return "Student [id=" + id + ", ]";
        }

        }
        application.xml中進(jìn)行配置,兩個bean:
        "stu1" class="com.wokao666.Student">




        "stu2" class="com.wokao666.Student">




        好了,接下來給最開頭那段代碼打個斷點 (Breakpoint):

        第一步:急切地加載ContextClosedEvent類,以避免在WebLogic 8.1中的應(yīng)用程序關(guān)閉時出現(xiàn)奇怪的類加載器問題。

        這一步無需太過在意!

        第二步:既然是new ClassPathXmlApplicationContext()?那么就調(diào)用構(gòu)造器嘛!

        第三步:

        第四步:

        好,我們跟著第三步中的super(parent),再結(jié)合上面第三節(jié)的第 6 小點 UML 圖一步一步跟蹤,然后我們來到AbstractApplicationContext的這個方法:
        那么里邊的resourcePatternResolver的類型是什么呢?屬于第三節(jié)說的 6 大步驟的哪個部分呢?通過跟蹤可以看到它的類型是ResourcePatternResolver類型的,而ResourcePatternResolver又是繼承了ResourceLoader接口,因此屬于加載資源模塊,如果還不清晰,咱們再看看ResourcePatternResolver的源碼即可,如下圖:
        對吧!不僅繼承ResourceLoader接口,而且只定義一個getResources()方法用于返回Resource[]資源集合。再者,這個接口還使用了策略模式,其具體的實現(xiàn)都在實現(xiàn)類當(dāng)中,好吧!來看看 UML 圖就知道了!
        PathMatchingResourcePatternResolver這個實現(xiàn)類呢!它就是用來解釋不同路徑資源的,比如你傳入的資源路徑有可能是一個常規(guī)的url, 又或者有可能是以classpath*前綴,都交給它處理。
        ServletContextResourcePatternResolver這個實現(xiàn)類顧名思義就是用來加載Servlet上下文的,通常用在 web 中。

        第五步:

        接著第四步的方法,我們在未進(jìn)入第四步的方法時,此時會對AbstractApplicationContext進(jìn)行實例化,此時this對象的某些屬性被初始化了(如日志對象),如下圖:
        接著進(jìn)入getResourcePatternResolver()方法:
        第四步說了,PathMatchingResourcePatternResolver用來處理不同的資源路徑的,怎么處理,我們先進(jìn)去看看!
        如果找到,此時控制臺會打印找到用于OSGi包URL解析的Equinox FileLocator日志。沒打印很明顯找不到!
        運行完成返回setParent()方法。

        第六步:

        如果父代是非null,,則該父代與當(dāng)前this應(yīng)用上下文環(huán)境合并。顯然這一步并沒有做什么事!parent顯然是null的,那么就不合并嘛!還是使用當(dāng)前this的環(huán)境。
        做個總結(jié):前六步基本上做了兩件事:
        • 1、初始化相關(guān)上下文環(huán)境,也就是初始化ClassPathXmlApplicationContext實例

        • 2、獲得一個resourcePatternResolver對象,方便第七步的資源解析成Resource對象

        第七步:

        第七步又回到剛開始第三步的代碼,因為我們前面 6 步已經(jīng)完成對super(parent)的追蹤。讓我們看看setConfigLocation()方法是怎么一回事~
        /**
        * Set the config locations for this application context.//未應(yīng)用上下文設(shè)置資源路徑
        *

        If not set, the implementation may use a default as appropriate.//如果未設(shè)置,則實現(xiàn)可以根據(jù)需要使用默認(rèn)值。
        */
        public void setConfigLocations(String... locations) {
        if (locations != null) {//非空
        Assert.noNullElements(locations, "Config locations must not be null");//斷言保證locations的每個元素都不為null
        this.configLocations = new String[locations.length];
        for (int i = 0; i < locations.length; i++) {
        this.configLocations[i] = resolvePath(locations[i]).trim();//去空格,很好奇resolvePath做了什么事情?
        }
        }
        else {
        this.configLocations = null;
        }
        }

        進(jìn)入resolvePath()方法看看:
        /**
        * 解析給定的資源路徑,必要時用相應(yīng)的環(huán)境屬性值替換占位符,應(yīng)用于資源路徑配置。
        * Resolve the given path, replacing placeholders with corresponding
        * environment property values if necessary. Applied to config locations.
        * @param path the original file path
        * @return the resolved file path
        * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
        */

        protected String resolvePath(String path) {
        return getEnvironment().resolveRequiredPlaceholders(path);
        }
        進(jìn)入getEnvironment()看看:
        /**
        * {@inheritDoc}
        *

        If {@code null}, a new environment will be initialized via
        * {@link #createEnvironment()}.
        */
        @Override
        public ConfigurableEnvironment getEnvironment() {
        if (this.environment == null) {
        this.environment = createEnvironment();
        }
        return this.environment;
        }

        進(jìn)入createEnvironment(), 方法,我們看到在這里創(chuàng)建了一個新的StandardEnviroment對象,它是Environment的實現(xiàn)類,表示容器運行的環(huán)境,比如 JDK 環(huán)境,Servlet 環(huán)境,Spring 環(huán)境等等,每個環(huán)境都有自己的配置數(shù)據(jù),如System.getProperties()System.getenv()等可以拿到 JDK 環(huán)境數(shù)據(jù);ServletContext.getInitParameter()可以拿到 Servlet 環(huán)境配置數(shù)據(jù)等等, 也就是說 Spring 抽象了一個Environment來表示環(huán)境配置。
        生成的StandardEnviroment對象并沒有包含什么內(nèi)容,只是一個標(biāo)準(zhǔn)的環(huán)境,所有的屬性都是默認(rèn)值。
        總結(jié):對傳入的path進(jìn)行路徑解析

        第八步:這一步是重頭戲

        先做個小結(jié):到現(xiàn)在為止,我們擁有了以下實例:
        現(xiàn)在代碼運行到如下圖的refresh()方法:
        看一下這個方法的內(nèi)容是什么?
        @Override
        public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
        // 刷新前準(zhǔn)備工作,包括設(shè)置啟動時間,是否激活標(biāo)識位,初始化屬性源(property source)配置
        prepareRefresh();

        // 創(chuàng)建beanFactory(過程是根據(jù)xml為每個bean生成BeanDefinition并注冊到生成的beanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        //準(zhǔn)備創(chuàng)建好的beanFactory(給beanFactory設(shè)置ClassLoader,設(shè)置SpEL表達(dá)式解析器,設(shè)置類型轉(zhuǎn)化器【能將xml String類型轉(zhuǎn)成相應(yīng)對象】,
        //增加內(nèi)置ApplicationContextAwareProcessor對象,忽略各種Aware對象,注冊各種內(nèi)置的對賬對象【BeanFactory,ApplicationContext】等,
        //注冊AOP相關(guān)的一些東西,注冊環(huán)境相關(guān)的一些bean
        prepareBeanFactory(beanFactory);

        try {
        // 模板方法,為容器某些子類擴展功能所用(工廠后處理器)這里可以參考BeanFactoryPostProcessor接口的postProcessBeanFactory方法
        postProcessBeanFactory(beanFactory);

        // 調(diào)用所有BeanFactoryPostProcessor注冊為Bean
        invokeBeanFactoryPostProcessors(beanFactory);

        // 注冊所有實現(xiàn)了BeanPostProcessor接口的Bean
        registerBeanPostProcessors(beanFactory);

        // 初始化MessageSource,和國際化相關(guān)
        initMessageSource();

        // 初始化容器事件傳播器
        initApplicationEventMulticaster();

        // 調(diào)用容器子類某些特殊Bean的初始化,模板方法
        onRefresh();

        // 為事件傳播器注冊監(jiān)聽器
        registerListeners();

        // 初始化所有剩余的bean(普通bean)
        finishBeanFactoryInitialization(beanFactory);

        // 初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件
        finishRefresh();
        }
        catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
        "cancelling refresh attempt: " + ex);
        }
        // 銷毀已創(chuàng)建的bean
        destroyBeans();
        // 重置`active`標(biāo)志
        cancelRefresh(ex);
        throw ex;
        }
        finally {
        //重置一些緩存
        resetCommonCaches();
        }
        }
        }
        在這里我想說一下,這個refresh()方法其實是一個模板方法, 很多方法都讓不同的實現(xiàn)類去實現(xiàn),但該類本身也實現(xiàn)了其中一些方法,并且這些已經(jīng)實現(xiàn)的方法是不允許子類重寫的,比如:prepareRefresh()方法。更多模板方法設(shè)計模式,可看我之前的文章?談一談我對‘模板方法’設(shè)計模式的理解(Template)。
        先進(jìn)入prepareRefresh()方法:
        /**
        * Prepare this context for refreshing, setting its startup date and
        * active flag as well as performing any initialization of property sources.
        */

        protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();//設(shè)置容器啟動時間
        this.closed.set(false);//容器關(guān)閉標(biāo)志,是否關(guān)閉?
        this.active.set(true);//容器激活標(biāo)志,是否激活?

        if (logger.isInfoEnabled()) {//運行到這里,控制臺就會打印當(dāng)前容器的信息
        logger.info("Refreshing " + this);
        }

        // 空方法,由子類覆蓋實現(xiàn),初始化容器上下文中的property文件
        initPropertySources();

        //驗證標(biāo)記為必需的所有屬性均可解析,請參閱ConfigurablePropertyResolver#setRequiredProperties
        getEnvironment().validateRequiredProperties();

        //允許收集早期的ApplicationEvents,一旦多播器可用,即可發(fā)布...
        this.earlyApplicationEvents = new LinkedHashSet();
        }
        控制臺輸出:
        三月 22, 2018 4:21:13 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
        信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@96532d6: startup date [Thu Mar 22 16:21:09 CST 2018]; root of context hierarchy

        第九步:

        進(jìn)入obtainFreshBeanFactory()方法:
        /**
        * 告訴子類刷新內(nèi)部bean工廠(子類是指AbstractApplicationContext的子類,我們使用的是ClassPathXmlApplicationContext)
        * Tell the subclass to refresh the internal bean factory.
        */

        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();//刷新Bean工廠,如果已經(jīng)存在Bean工廠,那就關(guān)閉并銷毀,再創(chuàng)建一個新的bean工廠
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();//獲取新創(chuàng)建的Bean工廠
        if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);//控制臺打印
        }
        return beanFactory;
        }
        進(jìn)入refreshBeanFactory()方法:
        /**
        * 該實現(xiàn)執(zhí)行該上下文的基礎(chǔ)Bean工廠的實際刷新,關(guān)閉以前的Bean工廠(如果有的話)以及為該上下文的生命周期的下一階段初始化新鮮的Bean工廠。
        * This implementation performs an actual refresh of this context's underlying
        * bean factory, shutting down the previous bean factory (if any) and
        * initializing a fresh bean factory for the next phase of the context's lifecycle.
        */

        @Override
        protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {//如果已有bean工廠
        destroyBeans();//銷毀
        closeBeanFactory();//關(guān)閉
        }
        try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();//創(chuàng)建一個新的bean工廠
        beanFactory.setSerializationId(getId());//為序列化目的指定一個id,如果需要,可以將此BeanFactory從此id反序列化回BeanFactory對象。
        //定制容器,設(shè)置啟動參數(shù)(bean可覆蓋、循環(huán)引用),開啟注解自動裝配
        customizeBeanFactory(beanFactory);
        ////將所有BeanDefinition載入beanFactory中,此處依舊是模板方法,具體由子類實現(xiàn)
        loadBeanDefinitions(beanFactory);
        //beanFactory同步賦值
        synchronized (this.beanFactoryMonitor) {
        this.beanFactory = beanFactory;
        }
        }
        catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
        }
        總結(jié):這一步主要的工作就是判斷刷新容器前是否已經(jīng)有 beanfactory 存在,如果有,那么就銷毀舊的 beanfactory, 那么就銷毀掉并且創(chuàng)建一個新的 beanfactory 返回給容器,同時將 xml 文件的BeanDefinition注冊到 beanfactory 中。如果不太清楚可以回過頭看看我們的第三節(jié)第5點內(nèi)容

        第十步:

        進(jìn)入第九步的loadBeanDefinitions(beanFactory)方法中去take a look:
        /**
        * 使用XmlBeanDefinitionReader來加載beandefnition,之前說過使用reader機制加載Resource資源變?yōu)锽eanDefinition對象
        * Loads the bean definitions via an XmlBeanDefinitionReader.
        * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
        * @see #initBeanDefinitionReader
        * @see #loadBeanDefinitions
        */

        @Override
        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 創(chuàng)建XmlBeanDefinitionReader對象
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // 使用當(dāng)前上下文Enviroment中的Resource配置beanDefinitionReader,因為beanDefinitionReader要將Resource解析成BeanDefinition嘛!
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        //初始化這個reader
        initBeanDefinitionReader(beanDefinitionReader);
        //將beandefinition注冊到工廠中(這一步就是將bean保存到Map中)
        loadBeanDefinitions(beanDefinitionReader);
        }
        控制臺輸出:
        三月 22, 2018 5:09:40 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
        信息: Loading XML bean definitions from class path resource [applicationContext.xml]

        第十一步:

        進(jìn)入prepareBeanFactory(beanFactory)方法:
        //設(shè)置bean類加載器
        //設(shè)置Spring語言表達(dá)式(SpEL)解析器
        //掃描ApplicationContextAware bean
        //注冊類加載期類型切面織入(AOP)LoadTimeWeaver
        //為各種加載進(jìn)入beanFactory的bean配置默認(rèn)環(huán)境

        第十二步:

        postProcessBeanFactory(beanFactory)方法:
        postProcessBeanFactory同樣作為一個模板方法,由子類來提供具體的實現(xiàn),子類可以有自己的特殊對BeanDefinition后處理方法,即子類可以在這對前面生成的BeanDefinition,即bean的元數(shù)據(jù)再處理。比如修改某個beanid/name屬性、scope屬性、lazy-init屬性等。

        第十三步:

        invokeBeanFactoryPostProcessors(beanFactory)方法:
        該方法調(diào)用所有的BeanFactoryPostProcessor,它是一個接口,實現(xiàn)了此接口的類需重寫postProcessBeanFactory()這個方法,可以看出該方法跟第十二步的方法是一樣的,只不過作為接口,更多的是提供給開發(fā)者來對生成的BeanDefinition做處理,由開發(fā)者提供處理邏輯。

        第十四步:

        其余剩下的方法基本都是像初始化消息處理源,初始化容器事件,注冊bean監(jiān)聽器到事件傳播器上,最后完成容器刷新。

        五、總結(jié)

        恭喜我,我終于寫完了,同樣也恭喜你,你也閱讀完了。
        我很佩服我自己能花這么長時間進(jìn)行總結(jié)發(fā)布,之所以要進(jìn)行總結(jié),那是因為小編還是贊同好記性不如爛筆頭的說法。
        你不記,你過陣子就會忘記,你若記錄,你過陣子也會忘記!區(qū)別在于忘記了,可以回過頭在很短的時間內(nèi)進(jìn)行回憶,查漏補缺,減少學(xué)習(xí)成本。
        再者,我認(rèn)為我分析的還不是完美的,缺陷很多,因此我將我寫的所有文章發(fā)布出來和大家探討交流,汕頭大學(xué)有校訓(xùn)說得非常地好,那就是說之知識是用來共享的,因為共享了,知識才能承前啟后。
        現(xiàn)在再梳理一下 Spring 初始化過程:
        • 1、首先初始化上下文,生成ClassPathXmlApplicationContext對象,在獲取resourcePatternResolver對象將xml解析成Resource對象。

        • 2、利用 1 生成的 context、resource 初始化工廠,并將 resource 解析成 beandefinition, 再將 beandefinition 注冊到 beanfactory 中。

        朋友們,發(fā)現(xiàn)毛病,請評論告訴小編,一起交流一起交流!

        推薦閱讀:

        【130期】面試官:你能說清楚分布式鎖,進(jìn)程鎖,線程鎖的區(qū)別嗎?

        【129期】看完這篇,再也不怕面試被問HashMap了~

        【128期】一道搜狗面試題:IO多路復(fù)用中select、poll、epoll之間的區(qū)別

        5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復(fù)「2048」,即可免費獲?。?!

        微信掃描二維碼,關(guān)注我的公眾號

        朕已閱?

        瀏覽 38
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            真人一级毛片 | 台湾精品无码 | 欧美一区二区三区婷婷五月 | 岛国大尺度做爰未删减欲动 | zztt黑暗传送门2025 | 天天射天天操天天爽 | 欧美透逼 | 久久久日韩精品无码一区二区 | 免费A级片视频 | 欧美黑屌|