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>

        Spring IoC 容器初始化

        共 8229字,需瀏覽 17分鐘

         ·

        2021-01-30 10:20

        概述

        上篇文章「Spring 中的 IoC 容器」從整體介紹了 Spring IoC 容器的相關(guān)概念和大致實(shí)現(xiàn)流程,本文要進(jìn)入源碼來(lái)一探究竟了。

        這里仍以前文的代碼為例進(jìn)行分析,測(cè)試代碼如下:

        public?class?IocTests?{
        ????@Test
        ????public?void?test01()?{
        ????????ApplicationContext?context?=?new?ClassPathXmlApplicationContext("application-ioc.xml");
        ????????System.out.println(context.getBean("person"));
        ????????System.out.println(context.getBean("dog"));
        ????}
        }

        /*
        ?*?輸出結(jié)果:
        ?*??Person{id=12,?name='Jack-12'}
        ?*??Dog{age=1}
        ?*/

        PS: 此處 Spring Framework 版本為 5.2.12.RELEASE,其他版本可能略有不同。

        代碼分析

        從 ClassPathXmlApplicationContext 的構(gòu)造器進(jìn)入它的代碼。

        ClassPathXmlApplicationContext 有很多重載的構(gòu)造器,不過(guò)多數(shù)都會(huì)調(diào)用到下面這個(gè):

        public?ClassPathXmlApplicationContext(
        ??String[]?configLocations,?boolean?refresh,?@Nullable?ApplicationContext?parent)

        ??throws?BeansException?
        {

        ?//?調(diào)用父類(lèi)構(gòu)造器,保存?zhèn)魅氲母溉萜?/span>
        ?super(parent);
        ????
        ?//?保存配置文件信息
        ?setConfigLocations(configLocations);
        ????
        ?//?刷新?IoC?容器
        ?if?(refresh)?{
        ??refresh();
        ?}
        }

        該構(gòu)造器主要做了三件事:

        1. 調(diào)用父類(lèi)的構(gòu)造器,保存?zhèn)魅氲母溉萜?/section>
        2. 保存配置信息,在本例中就是 application-ioc.xml
        3. 刷新 IoC 容器

        其中最核心的就是第三步,也是最復(fù)雜的。

        由于 ClassPathXmlApplicationContext 的整體繼承結(jié)構(gòu)比較復(fù)雜,為了便于分析其核心實(shí)現(xiàn),這里先暫時(shí)忽略它實(shí)現(xiàn)的接口,只看它的類(lèi)繼承結(jié)構(gòu):

        前面兩個(gè)步驟的代碼不再深入分析,這里直接進(jìn)入第三步,也就是 refresh 方法(該方法是在 AbstractApplicationContext 類(lèi)中實(shí)現(xiàn)的):

        public?abstract?class?AbstractApplicationContext?extends?DefaultResourceLoader
        ??implements?ConfigurableApplicationContext?
        {
        ?//?...

        ?@Override
        ?public?void?refresh()?throws?BeansException,?IllegalStateException?{
        ??synchronized?(this.startupShutdownMonitor)?{
        ???//?Prepare?this?context?for?refreshing.
        ???prepareRefresh();

        ???//?獲取?BeanFactory
        ???//?Tell?the?subclass?to?refresh?the?internal?bean?factory.
        ???ConfigurableListableBeanFactory?beanFactory?=?obtainFreshBeanFactory();

        ???//?Prepare?the?bean?factory?for?use?in?this?context.
        ???prepareBeanFactory(beanFactory);

        ???try?{
        ????//?Allows?post-processing?of?the?bean?factory?in?context?subclasses.
        ????postProcessBeanFactory(beanFactory);

        ????//?Invoke?factory?processors?registered?as?beans?in?the?context.
        ????invokeBeanFactoryPostProcessors(beanFactory);

        ????//?Register?bean?processors?that?intercept?bean?creation.
        ????registerBeanPostProcessors(beanFactory);

        ????//?Initialize?message?source?for?this?context.
        ????initMessageSource();

        ????//?Initialize?event?multicaster?for?this?context.
        ????initApplicationEventMulticaster();

        ????//?Initialize?other?special?beans?in?specific?context?subclasses.
        ????onRefresh();

        ????//?Check?for?listener?beans?and?register?them.
        ????registerListeners();

        ????//?Instantiate?all?remaining?(non-lazy-init)?singletons.
        ????finishBeanFactoryInitialization(beanFactory);

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

        ???//?catch?...
        ???//?finally?...
        ??}
        ?}

        ?//?...

        ?protected?ConfigurableListableBeanFactory?obtainFreshBeanFactory()?{
        ??refreshBeanFactory();
        ??return?getBeanFactory();
        ?}????
        }

        refresh 方法里面封裝了很多方法,每個(gè)方法里面又是一大堆代碼……

        剛開(kāi)始看到這里可能會(huì)被嚇到(我當(dāng)初就是這樣被勸退的)。

        其實(shí)不必,代碼雖然很多,但讀起來(lái)還是有跡可循的:就是要先找到一條主線。就像一棵樹(shù),先找到樹(shù)干,其它的都是細(xì)枝末節(jié)。先主后次,要不很容易陷進(jìn)去、讀起來(lái)一團(tuán)亂麻。

        PS: 之前寫(xiě)過(guò)一篇如何閱讀 JDK 源碼的文章「我是如何閱讀JDK源碼的?」,有興趣可以參考一下。

        但二者又有些不同:JDK 源碼相對(duì)獨(dú)立,一般關(guān)聯(lián)性不大,而 Spring 的代碼前后關(guān)聯(lián)太多。

        這里的主線是什么呢?

        就是 obtainFreshBeanFactory 方法(它的實(shí)現(xiàn)在 AbstractRefreshableApplicationContext 類(lèi)中),如下:

        public?abstract?class?AbstractRefreshableApplicationContext?extends?AbstractApplicationContext?{
        ?//?...

        ?@Override
        ?protected?final?void?refreshBeanFactory()?throws?BeansException?{
        ??//?如果容器已經(jīng)存在,則銷(xiāo)毀容器中的對(duì)象、關(guān)閉容器
        ??if?(hasBeanFactory())?{
        ???destroyBeans();
        ???closeBeanFactory();
        ??}
        ??try?{
        ???//?創(chuàng)建?BeanFactory
        ???DefaultListableBeanFactory?beanFactory?=?createBeanFactory();
        ???beanFactory.setSerializationId(getId());
        ???customizeBeanFactory(beanFactory);
        ???//?加載?BeanDefinition
        ???loadBeanDefinitions(beanFactory);
        ???this.beanFactory?=?beanFactory;
        ??}
        ??catch?(IOException?ex)?{
        ???throw?new?ApplicationContextException("I/O?error?parsing?bean?definition?source?for?"?+?getDisplayName(),?ex);
        ??}
        ?}

        ?//?...
        ????
        ?protected?abstract?void?loadBeanDefinitions(DefaultListableBeanFactory?beanFactory)
        ???throws?BeansException,?IOException
        ;????
        }

        refreshBeanFactory 看名字可以推測(cè)是刷新 IoC 容器,它主要做了三件事:

        1. 如果 IoC 容器已經(jīng)存在,則銷(xiāo)毀容器中的對(duì)象、關(guān)閉容器(正如其名,refresh,是一個(gè)全新的,就要先把舊的干掉)。
        2. 創(chuàng)建 BeanFactory,即 DefaultListableBeanFactory,它就是 Spring IoC 容器的默認(rèn)實(shí)現(xiàn)。
        3. 加載 BeanDefinition,也就是從配置文件(application-ioc.xml)中加載我們定義的 bean 信息,這一步也是最復(fù)雜的。

        這里的 loadBeanDefinitions 是一個(gè)抽象方法?!

        是的。這就是設(shè)計(jì)模式中的「模板模式(Template Pattern)」,這個(gè)模式不難理解,不了解也不影響,有空可以再看。

        JDK 中的 AbstractQueuedSynchronizer(AQS) 也使用了模板模式,前文「JDK源碼分析-AbstractQueuedSynchronizer(2)」可以參考。

        loadBeanDefinitions 方法其實(shí)就是把實(shí)現(xiàn)代碼放在子類(lèi)中了。

        What?它的子類(lèi)有那么多,哪個(gè)才是真·兒子呢?

        還記得上面那個(gè)類(lèi)的繼承結(jié)構(gòu)圖嗎?

        所以,這里它的子類(lèi)實(shí)現(xiàn)指的就是 AbstractXmlApplicationContext#loadBeanDefinitions 方法:

        public?abstract?class?AbstractXmlApplicationContext?extends?AbstractRefreshableConfigApplicationContext?{
        ?//?...

        ?@Override
        ?protected?void?loadBeanDefinitions(DefaultListableBeanFactory?beanFactory)?throws?BeansException,?IOException?{
        ??//?創(chuàng)建?BeanDefinitionReader,用于讀取?BeanDefinition
        ??//?Create?a?new?XmlBeanDefinitionReader?for?the?given?BeanFactory.
        ??XmlBeanDefinitionReader?beanDefinitionReader?=?new?XmlBeanDefinitionReader(beanFactory);

        ??//?Configure?the?bean?definition?reader?with?this?context's
        ??//?resource?loading?environment.
        ??beanDefinitionReader.setEnvironment(this.getEnvironment());
        ??beanDefinitionReader.setResourceLoader(this);
        ??beanDefinitionReader.setEntityResolver(new?ResourceEntityResolver(this));

        ??//?Allow?a?subclass?to?provide?custom?initialization?of?the?reader,
        ??//?then?proceed?with?actually?loading?the?bean?definitions.
        ??initBeanDefinitionReader(beanDefinitionReader);
        ??loadBeanDefinitions(beanDefinitionReader);
        ?}

        ?//?...
        ????
        ?protected?void?loadBeanDefinitions(XmlBeanDefinitionReader?reader)?throws?BeansException,?IOException?{
        ??//?讀取?Resource?類(lèi)型的配置
        ??Resource[]?configResources?=?getConfigResources();
        ??if?(configResources?!=?null)?{
        ???reader.loadBeanDefinitions(configResources);
        ??}
        ??//?讀取?String?類(lèi)型的配置
        ??String[]?configLocations?=?getConfigLocations();
        ??if?(configLocations?!=?null)?{
        ???reader.loadBeanDefinitions(configLocations);
        ??}
        ?}????
        }

        它來(lái)了,XmlBeanDefinitionReader,是用來(lái)讀取 BeanDefinition 的。

        這里 BeanDefinition 的類(lèi)型有兩種:Resource 和 String。其實(shí)本質(zhì)上還是一種,即 Resource,String 最終還是要轉(zhuǎn)為 Resource。

        這兩個(gè) loadBeanDefinitions 最終都會(huì)調(diào)用 XmlBeanDefinitionReader#loadBeanDefinitions 方法:

        public?class?XmlBeanDefinitionReader?extends?AbstractBeanDefinitionReader?{
        ?//?...

        ?@Override
        ?public?int?loadBeanDefinitions(Resource?resource)?throws?BeanDefinitionStoreException?{
        ??return?loadBeanDefinitions(new?EncodedResource(resource));
        ?}

        ?public?int?loadBeanDefinitions(EncodedResource?encodedResource)?throws?BeanDefinitionStoreException?{
        ??Assert.notNull(encodedResource,?"EncodedResource?must?not?be?null");
        ??if?(logger.isTraceEnabled())?{
        ???logger.trace("Loading?XML?bean?definitions?from?"?+?encodedResource);
        ??}

        ??Set?currentResources?=?this.resourcesCurrentlyBeingLoaded.get();

        ??if?(!currentResources.add(encodedResource))?{
        ???throw?new?BeanDefinitionStoreException(
        ?????"Detected?cyclic?loading?of?"?+?encodedResource?+?"?-?check?your?import?definitions!");
        ??}

        ??//?讀取配置文件
        ??try?(InputStream?inputStream?=?encodedResource.getResource().getInputStream())?{
        ???InputSource?inputSource?=?new?InputSource(inputStream);
        ???if?(encodedResource.getEncoding()?!=?null)?{
        ????inputSource.setEncoding(encodedResource.getEncoding());
        ???}
        ???//?實(shí)際加載?BeanDefinition
        ???return?doLoadBeanDefinitions(inputSource,?encodedResource.getResource());
        ??}
        ??//?catch?...
        ?}

        ?protected?int?doLoadBeanDefinitions(InputSource?inputSource,?Resource?resource)
        ???throws?BeanDefinitionStoreException?
        {

        ??try?{
        ???//?將?Resource?解析為?Document?對(duì)象
        ???Document?doc?=?doLoadDocument(inputSource,?resource);
        ???//?從?Document?解析和注冊(cè)?BeanDefinition
        ???int?count?=?registerBeanDefinitions(doc,?resource);
        ???if?(logger.isDebugEnabled())?{
        ????logger.debug("Loaded?"?+?count?+?"?bean?definitions?from?"?+?resource);
        ???}
        ???return?count;
        ??}
        ??//?catch?...
        ?}????

        ?//?...
        }

        loadBeanDefinitions 做了層封裝,主要工作是在 doLoadBeanDefinitions 實(shí)現(xiàn)的。doLoadBeanDefinitions 方法主要做了兩件事:

        1. 將 Resource 解析為 Document 對(duì)象。
        2. 從 Document 解析和注冊(cè) BeanDefinition。

        第一步只是工具人,不必深究。

        關(guān)鍵在于第二步,也比較復(fù)雜,后文再分析吧,畢竟源碼讀起來(lái)還是有點(diǎn)累……本文就先到這了。

        Spring 源碼中的關(guān)鍵部分通常是有點(diǎn)小規(guī)律的,比如:

        1. 以 try...catch 包圍
        2. 以 do 開(kāi)頭的方法才是實(shí)際做事的,前面都是 caller,比如 doLoadBeanDefinitions、doGetBean 等

        其他規(guī)律小伙伴們可以繼續(xù)補(bǔ)充。

        小結(jié)

        本文開(kāi)始進(jìn)入 Spring IoC 容器的源碼了,主要找到了一條主線,為便于對(duì)整體有個(gè)了解,這里簡(jiǎn)單總結(jié)了一個(gè)思維導(dǎo)圖(還有后面的預(yù)告):


        小伙伴們讀的時(shí)候也可以用思維導(dǎo)圖工具畫(huà)一下主要流程,起到提綱挈領(lǐng)的作用,也便于以后回顧。

        源碼讀起來(lái)還是需要一點(diǎn)耐心的,畢竟它就是這么樸實(shí)無(wú)華……且枯燥??

        瀏覽 71
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        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>
            精品久久黄 | 永久免费精品二区三区 | AV片在线观看 | 能看黄色电影的网站 | 欧美乱大交做爰3XXX无码性色情 | 久久AV无码精品人妻系列老妇 | 深夜免费福利视频 | 麻豆影音先锋 | 五月天激情亚洲 | 天天AAA无码精品级 |