<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring IoC 容器初始化

          共 8229字,需瀏覽 17分鐘

           ·

          2021-01-30 10:20

          概述

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

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

          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)造器,不過多數(shù)都會調(diào)用到下面這個:

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

          ??throws?BeansException?
          {

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

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

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

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

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

          前面兩個步驟的代碼不再深入分析,這里直接進(jìn)入第三步,也就是 refresh 方法(該方法是在 AbstractApplicationContext 類中實現(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 方法里面封裝了很多方法,每個方法里面又是一大堆代碼……

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

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

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

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

          這里的主線是什么呢?

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

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

          ?@Override
          ?protected?final?void?refreshBeanFactory()?throws?BeansException?{
          ??//?如果容器已經(jīng)存在,則銷毀容器中的對象、關(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 看名字可以推測是刷新 IoC 容器,它主要做了三件事:

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

          這里的 loadBeanDefinitions 是一個抽象方法?!

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

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

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

          What?它的子類有那么多,哪個才是真·兒子呢?

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

          所以,這里它的子類實現(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?類型的配置
          ??Resource[]?configResources?=?getConfigResources();
          ??if?(configResources?!=?null)?{
          ???reader.loadBeanDefinitions(configResources);
          ??}
          ??//?讀取?String?類型的配置
          ??String[]?configLocations?=?getConfigLocations();
          ??if?(configLocations?!=?null)?{
          ???reader.loadBeanDefinitions(configLocations);
          ??}
          ?}????
          }

          它來了,XmlBeanDefinitionReader,是用來讀取 BeanDefinition 的。

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

          這兩個 loadBeanDefinitions 最終都會調(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());
          ???}
          ???//?實際加載?BeanDefinition
          ???return?doLoadBeanDefinitions(inputSource,?encodedResource.getResource());
          ??}
          ??//?catch?...
          ?}

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

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

          ?//?...
          }

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

          1. 將 Resource 解析為 Document 對象。
          2. 從 Document 解析和注冊 BeanDefinition。

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

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

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

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

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

          小結(jié)

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


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

          源碼讀起來還是需要一點耐心的,畢竟它就是這么樸實無華……且枯燥??

          瀏覽 71
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  六区,七区视频在线播放 | 人人舔人人插 | 大黄网站在线观看 | 樱桃香蕉网站在线观看 | 一级黄在线看看 |