Spring IoC - IoC 容器初始化 源碼解析

undefined

前言

本章主要內(nèi)容是由以下部分組成,

  • Spring 中容器初始化入口
  • 以最經(jīng)典的ClassPathXmlApplicationContext 為例,講解Spring IoC 的容器初始化過程

在學習源碼的過程當中,我想強調(diào)兩點:

  1. 一定要學會抓重點僻他,歸納核心類、核心方法、核心步驟坤候。
  2. 理解類、變量企蹭、方法名的命名白筹,Spring 源碼的命名是很講究的,很多時候是自解釋的
  3. 一定要學會看Java doc 谅摄,同上徒河,這種頂級的框架的java doc 描述非常的詳盡

Spring 容器初始化入口

啟動容器,實際上指的就是實例化ApplicationContext的這個動作送漠。只是在不同情況下可能有不同的表現(xiàn)形式顽照。

  1. ClassPathXmlApplicationContext 通過XML配置
ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");
  1. AnnotationConfigApplicationContext 通過java config 類配置
@Configuration
@ComponentScan("ric.study.demo.ioc")
public class BeanDemoConfig {
    public static void main(String... strings) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(BeanDemoConfig.class);
        System.out.println("Spring container started and is ready");
        ...
    }
}

類似前面這兩種new ***ApplicationContext的方式,很少會用于直接的生產(chǎn)開發(fā)。一般都是我們自己在demo中或者單元測試中會用到代兵。

  1. WebApplicationContext SpringMVC

這個實際上是我們平常最常用的初始化方式尼酿,Spring MVC 中 ServletContext 為 Spring 的 IoC容器提供了宿主環(huán)境。是通過ContextLoaderListener 的初始化來建立的植影。

WebApplicationContext 的初始化調(diào)用鏈路:ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> ContextLoader.createWebApplicationContext --> ContextLoader.determineContextClass --> ContextLoader.determineContextClass裳擎。

底層是通過反射來實例化的。

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        Class<?> contextClass = determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

這塊內(nèi)容先簡要提一下思币,屬于SpringMVC的內(nèi)容鹿响,不是我們今天要講的Spring IoC 模塊的知識。

容器初始化 源碼解析

現(xiàn)在讓我們正式開始的源碼解讀谷饿。會從最經(jīng)典的ClassPathXmlApplicationContext 上下文為起點惶我,來描述整個過程。

在說明之前各墨,我想了想還是覺得把整個IoC容器初始化的關(guān)鍵步驟為大家梳理一下指孤,以便于大家能在心里有個大概的脈絡(luò),更容易讀懂源碼贬堵,更容易抓住重點恃轩。再重復(fù)提一句,看源碼一定要學會抓重點黎做,歸納核心類叉跛、核心方法、核心步驟蒸殿。

ClassPathXmlApplicationContext 的容器初始化我們大致分為下面幾步:

  1. BeanDefinition 的 Resource 定位

    這里的Resource定位 是通過繼承ResourceLoader 獲得的筷厘,ResourceLoader代表了加載資源的一種方式,正是策略模式的實現(xiàn)宏所。

  2. 從 Resource中解析酥艳、載入BeanDefinition

  3. BeanDefinition 在IoC 容器中的注冊

前面說了,實例化這個上下文爬骤,就是在啟動 IoC 容器充石。那我們肯定要從它的構(gòu)造函數(shù)入手。

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

入?yún)⒅械?code>configLocations在這里就是你XML配置文件 的 classpath霞玄。

setConfigLocations(configLocations);我這里不展開講骤铃,內(nèi)容不復(fù)雜,就是把一些帶有占位符的地址解析成實際的地址坷剧。

再之后就是refresh()惰爬,我們說的容器初始化,就是在這里面進行的惫企,這里取名為refresh撕瞧,是因為容器啟動之后,再調(diào)用refresh()會刷新IoC 容器

這里先放上IoC容器初始化的時序圖风范,方便理解咨跌,

loadBeanDefinitions.png

refresh() 的源碼:

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 來個鎖,不然 refresh() 還沒結(jié)束硼婿,你又來個啟動或銷毀容器的操作锌半,那不就亂套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 準備工作,記錄下容器的啟動時間寇漫、標記“已啟動”狀態(tài)刊殉、處理配置文件中的占位符
      prepareRefresh();

      // 這步比較關(guān)鍵,這步完成后州胳,配置文件就會解析成一個個 Bean 定義记焊,注冊到 BeanFactory 中,
      // 當然栓撞,這里說的 Bean 還沒有初始化遍膜,只是配置信息都提取出來了,
      // 注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設(shè)置 BeanFactory 的類加載器瓤湘,添加幾個 BeanPostProcessor瓢颅,手動注冊幾個特殊的 bean
      // 這塊待會會展開說
      prepareBeanFactory(beanFactory);

      try {
         // 【這里需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現(xiàn)了此接口弛说,
         // 那么在容器初始化以后挽懦,Spring 會負責調(diào)用里面的 postProcessBeanFactory 方法∧救耍】

         // 這里是提供給子類的擴展點信柿,到這里的時候,所有的 Bean 都加載醒第、注冊完成了渔嚷,但是都還沒有初始化
         // 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現(xiàn)類或做點什么事
         postProcessBeanFactory(beanFactory);
         // 調(diào)用 BeanFactoryPostProcessor 各個實現(xiàn)類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注冊 BeanPostProcessor 的實現(xiàn)類,注意看和 BeanFactoryPostProcessor 的區(qū)別
         // 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個方法分別在 Bean 初始化之前和初始化之后得到執(zhí)行稠曼。注意形病,到這里 Bean 還沒初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化當前 ApplicationContext 的 MessageSource,國際化不是重點蒲列,不展開
         initMessageSource();

         // 初始化當前 ApplicationContext 的事件廣播器,這里也不展開了
         initApplicationEventMulticaster();

         // 從方法名就可以知道搀罢,典型的模板方法(鉤子方法)蝗岖,
         // 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
         onRefresh();

         // 注冊事件監(jiān)聽器,監(jiān)聽器需要實現(xiàn) ApplicationListener 接口榔至。這也不是我們的重點,過
         registerListeners();

         // 重點,重點筝闹,重點
         // 初始化所有的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最后,廣播事件划提,ApplicationContext 初始化完成
         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.
         // 銷毀已經(jīng)初始化的 singleton 的 Beans,以免有些 bean 會一直占用資源
         destroyBeans();
         // Reset 'active' flag.
         cancelRefresh(ex);
         throw ex;
      }

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

我會從上述流程中邢享,挑以下幾個進行分析鹏往,

  1. prepareRefresh() 創(chuàng)建容器前的準備工作
  2. obtainFreshBeanFactory() 創(chuàng)建 BeanFactory
  3. prepareBeanFactory(beanFactory) 對BeanFactory進行一些特征的設(shè)置工作
  4. finishBeanFactoryInitialization(beanFactory); 初始化所有的 singleton beans(DI的入口)

1. prepareRefresh() 創(chuàng)建容器前的準備工作

protected void prepareRefresh() {
   // 記錄啟動時間,
   // 將 active 屬性設(shè)置為 true骇塘,closed 屬性設(shè)置為 false伊履,它們都是 AtomicBoolean 類型
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   initPropertySources();

   // 校驗 xml 配置文件
   getEnvironment().validateRequiredProperties();

   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

2.★ obtainFreshBeanFactory() 創(chuàng)建 Bean 容器,加載并注冊 Bean

IoC初始化里面最重要的部分款违。

關(guān)鍵是以下幾步唐瀑,

  • 初始化BeanFactory
  • 加載Bean
  • 注冊Bean
  • ...

注意:這步完成后,Bean 并沒有完成初始化插爹,實際的實例并沒有被創(chuàng)建哄辣。

源碼位置:AbstractApplicationContext#obtainFreshBeanFactory()

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 關(guān)閉舊的 BeanFactory (如果有),創(chuàng)建新的 BeanFactory赠尾,加載 Bean 定義力穗、注冊 Bean 等等
        refreshBeanFactory();
        // 返回上一步剛剛創(chuàng)建的BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

源碼位置:AbstractRefreshableApplicationContext#refreshBeanFactory()

    protected final void refreshBeanFactory() throws BeansException {
        // 如果 ApplicationContext 已經(jīng)加載過 BeanFactory,銷毀所有的Bean萍虽,關(guān)閉BeanFactory
        // 注意點:應(yīng)用中BeanFactory是可以有多個的睛廊,這里可不是說全局是否有BeanFactory
        // 而是說當前的ApplicationContext有沒有BeanFactory!
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 初始化一個 DefaultListableBeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            // 用于 BeanFactory 的序列化杉编,一般人應(yīng)該用不到吧...
            beanFactory.setSerializationId(getId());
            // 下面是兩個重點方法
            // 設(shè)置 BeanFactory 的兩個重要屬性
            // 是否允許 Bean 覆蓋超全、是否允許循環(huán)引用 TODO 2.1
            customizeBeanFactory(beanFactory);
            
            // 加載BeanDefinition到BeanFactory  單獨拉出來講
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

看到這里的時候,可以感覺到一個設(shè)計思路邓馒,ApplicationContext 繼承自 BeanFactory嘶朱,但是它不應(yīng)該被理解為 BeanFactory 的實現(xiàn)類,而是說其內(nèi)部持有一個實例化的 BeanFactory(DefaultListableBeanFactory)光酣。以后所有的 BeanFactory 相關(guān)的操作其實是委托給這個實例來處理的疏遏。

2.1 customizeBeanFactory

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

BeanDefinition 的覆蓋問題可能會有開發(fā)者碰到這個坑,就是在配置文件中定義 bean 時使用了相同的 id 或 name救军,默認情況下财异,allowBeanDefinitionOverriding 屬性為 null(Boolean類型),如果在同一配置文件中重復(fù)了唱遭,會拋錯戳寸,但是如果不是同一配置文件中,會發(fā)生覆蓋拷泽。

循環(huán)引用也很好理解:A 依賴 B疫鹊,而 B 依賴 A袖瞻。或 A 依賴 B拆吆,B 依賴 C聋迎,而 C 依賴 A。

默認情況下枣耀,Spring 允許循環(huán)依賴霉晕,當然如果你在 A 的構(gòu)造方法中依賴 B,在 B 的構(gòu)造方法中依賴 A 是不行的奕枢。

2.2 ★ loadBeanDefinitions(beanFactory) 加載BeanDefinition

看下這個方法的聲明娄昆,

    /**
     * Load bean definitions into the given bean factory, typically through
     * delegating to one or more bean definition readers.
     * @param beanFactory the bean factory to load bean definitions into
     * @throws BeansException if parsing of the bean definitions failed
     * @throws IOException if loading of bean definition files failed
     * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
     * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
     */
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException;

在ClassPathXmlApplicationContext 是按照解析XML的加載方式》毂颍看javadoc的描述萌焰,是通過XmlBeanDefinitionReader來載入Bean Definitions。

    /**
     * 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 {
        // 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,
        // 初始化Reader 不重要谷浅,看下這個方法的javadoc就很好理解了
        initBeanDefinitionReader(beanDefinitionReader);
        // 真正重要的步驟0歉!
        // 用Reader去加載XML配置
        loadBeanDefinitions(beanDefinitionReader);
    }

loadBeanDefinitions(beanDefinitionReader)

    /**
     * Load the bean definitions with the given XmlBeanDefinitionReader.
     * 看這句注釋:this method is just supposed to load and/or register bean definitions.
     */
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            // 這個分支一疯,通過路徑名去獲取Resource撼玄,會和上面的方法殊途同歸
            reader.loadBeanDefinitions(configLocations);
        }
    }

AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)

    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            // 遍歷解析XML文件,加載 BeanDefinition
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }

接下去的源碼不細講墩邀,這里載入分為兩大步掌猛,

  1. 一就是通過調(diào)用XML的解析器獲取到 document 對象,完成通用XML解析眉睹;
  2. 二就是按照Spring的Bean規(guī)則進行解析荔茬。Spring的Bean規(guī)則進行解析這個過程是BeanDefinitionDocumentReader來實現(xiàn)的,里面包含了各種Spring Bean定義規(guī)則的處理竹海。

這里我覺得核心知識點就是Spring Bean規(guī)則的解析慕蔚,簡單點來說,里面包含了我們在XML配置的那些信息斋配,怎么解析成容器中 BeanDefinition的規(guī)則和步驟孔飒。這部分由于和主要流程關(guān)系不大,我就沒貼源碼解析了艰争,會占掉很大的篇幅坏瞄,影響閱讀和理解。

在這因為Spring 的 Bean配置方式有很多甩卓,解析配置信息到BeanDefinition的實現(xiàn)方式也有很多鸠匀,XML又是現(xiàn)在少用的方式,所以關(guān)于XML中的Spring Bean規(guī)則的解析的詳細源碼就先略過了猛频。有興趣的同學可以閱讀《Spring 技術(shù)內(nèi)幕》這本書或者其他的文章書籍狮崩。

2.3 注冊Bean

雖然上面說了不講XML 解析 成 BeanDefinition的過程源碼。但是上述loadBeanDefinitions(resource)包含了我們關(guān)鍵的第三步鹿寻,注冊Bean睦柴。這部分還是需要填一下的。

注意一下前面實例化Reader的代碼毡熏,

    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    /**
     * Create new XmlBeanDefinitionReader for the given bean factory.
     * @param registry the BeanFactory to load bean definitions into,
     * in the form of a BeanDefinitionRegistry
     */
    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

beanDefinitionReader 獲取到 beanFactory 的引用坦敌,這個引用會在beanDefinition 被加載完畢要注冊的時候使用到×》ǎ可以看到是因為BeanDefinitionRegistry這個接口狱窘,賦予了BeanFactory注冊BeanDefinition的特性。

具體執(zhí)行“注冊Bean”這一動作的源碼财搁,按照上述loadBeanDefinitions(resource)方法一直走下去的話是在DefaultBeanDefinitionDocumentReader#processBeanDefinition()方法中蘸炸,

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                // 注冊Bean
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

源碼位置BeanDefinitionReaderUtils#registerBeanDefinition()

    /**
     * Register the given bean definition with the given bean factory.
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        // 注冊
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        // 如果還有別名的,把別名全都注冊一遍
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                // 到時候獲取的時候尖奔,就是先把Alias轉(zhuǎn)化成BeanName搭儒,再去獲取對應(yīng)的Bean
                registry.registerAlias(beanName, alias);
            }
        }
    }

上面的registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());,

源碼位置DefaultListableBeanFactory#registerBeanDefinition()

@Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        // 注意哦,這里是有關(guān) “允許Bean覆蓋” 的邏輯代碼
        // 記得這個配置 allowBeanDefinitionOverriding
        BeanDefinition oldBeanDefinition;

        // beanDefinitionMap 是存放所有BeanDefinition的容器
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        
        // not null 說明提茁,有重復(fù)名稱的bean
        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                // 判斷是否允許覆蓋淹禾,不允許直接拋異常
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // 打下debug log...用框架定義的 Bean 覆蓋用戶自定義的 Bean
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                // 打下debug log...用新的 Bean 覆蓋舊的 Bean
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                // 打下debug log...用同等的 Bean 覆蓋舊的 Bean
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 覆蓋了
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            // 判斷是否有其他Bean已經(jīng)開始初始化了
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                // 檢測創(chuàng)建 Bean 階段已經(jīng)開啟,需要對 beanDefinitionMap 進行并發(fā)控制
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                // 最最最正常的分支
                
                //  注冊 到 容器中
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 這是一個 ArrayList茴扁,所以會按照 bean 配置的順序保存每一個注冊的 Bean 的名字
                this.beanDefinitionNames.add(beanName);
                // 這是個 LinkedHashSet铃岔,代表的是手動注冊的 singleton bean,
                // 注意這里是 remove 方法峭火,到這里的 Bean 當然不是手動注冊的
                // 手動指的是通過調(diào)用以下方法注冊的 bean :
                // registerSingleton(String beanName, Object singletonObject)
                // 這不是重點毁习,解釋只是為了不讓大家疑惑。Spring 會在后面"手動"注冊一些 Bean躲胳,
                // 如 "environment"蜓洪、"systemProperties" 等 bean,我們自己也可以在運行時注冊 Bean 到容器中的
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

以上坯苹,只是obtainFreshBeanFactory()的內(nèi)容隆檀,到這里,BeanFactory也算是實例化完成了粹湃。


這里還是來個分割線恐仑。因為接下去會講refresh() 方法的后續(xù)步驟的知識點,我想讀者同學在這里最好最好为鳄,回到 前面refresh()總述的部分裳仆,再看一下。(如果你是一邊還打開著IDE孤钦,在對照閱讀調(diào)試的話歧斟,回到最前面refresh() 方法纯丸,再繼續(xù)往下)

3. prepareBeanFactory(beanFactory)

此方法負責對BeanFactory進行一些特征的設(shè)置工作,這些特征在代碼中都有體現(xiàn)静袖。

    /**
     * Configure the factory's standard context characteristics,
     * such as the context's ClassLoader and post-processors.
     * @param beanFactory the BeanFactory to configure
     */
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        // BeanFactory 需要加載類觉鼻,所以需要獲得類加載器
        // 設(shè)置當前ApplicationContext的類加載器
        beanFactory.setBeanClassLoader(getClassLoader());
        // 內(nèi)含 Spel 解釋器,暫時不重要
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        // 注冊屬性編輯器队橙,暫時不重要
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        // 添加一個ApplicationContextAwareProcessor坠陈,主要針對實現(xiàn)了Aware接口的Bean
        // 延伸知識:在Spring中我們自己的bean可以通過實現(xiàn)EnvironmentAware等一系列Aware接口,獲取到Spring內(nèi)部的一些對象捐康。
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        // 依賴解析忽略, 設(shè)置哪些接口在進行依賴注入的時候應(yīng)該被忽略
        // 通俗來說仇矾,下面幾行的意思就是,如果某個 bean 依賴于以下幾個接口的實現(xiàn)類解总,在自動裝配的時候忽略它們贮匕,
        // Spring 會通過其他方式來處理這些依賴。
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        /**
        * 下面幾行就是為特殊的幾個 bean 賦值花枫,如果有 bean 依賴了以下幾個粗合,會注入這邊相應(yīng)的值,
        * 之前我們說過乌昔,"當前 ApplicationContext 持有一個 BeanFactory"隙疚,這里解釋了第一行
        * ApplicationContext 還繼承了 ResourceLoader、ApplicationEventPublisher磕道、MessageSource
        * 所以對于這幾個依賴供屉,可以賦值為 this,注意 this 是一個 ApplicationContext
        * 那這里怎么沒看到為 MessageSource 賦值呢溺蕉?那是因為 MessageSource 被注冊成為了一個普通的 bean
        */
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // Register early post-processor for detecting inner beans as ApplicationListeners.
        // 這個 BeanPostProcessor 也很簡單伶丐,在 bean 實例化后,如果是 ApplicationListener 的子類疯特,
        // 這個postProcessor的作用就是將其添加到 listener 列表中哗魂,可以理解成:注冊 事件監(jiān)聽器
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        // 這里涉及到特殊的 bean录别,名為:loadTimeWeaver邻吞,AspectJ相關(guān)內(nèi)容
        // 不是這里的重點组题,放過我
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }
        // Register default environment beans.
        // Spring 的“智能”操作,會幫我們默認注冊一些有用的Bean
        //  如果沒有定義 "environment" 這個 bean崔列,那么 Spring 會 "手動" 注冊一個
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        // 如果沒有定義 "systemProperties" 這個 bean旺遮,那么 Spring 會 "手動" 注冊一個
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        // 如果沒有定義 "systemEnvironment" 這個 bean赵讯,那么 Spring 會 "手動" 注冊一個
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

4. ★ finishBeanFactoryInitialization(beanFactory) 實例化所有單例

這里會負責初始化所有的 singleton beans盈咳。

Spring 會在這個階段完成所有的 singleton beans 的實例化猪贪。

到目前為止讯私,應(yīng)該說 BeanFactory 已經(jīng)創(chuàng)建完成西傀,并且所有的實現(xiàn)了 BeanFactoryPostProcessor 接口的 Bean 都已經(jīng)初始化并且其中的 postProcessBeanFactory(factory)方法已經(jīng)得到回調(diào)執(zhí)行了。而且 Spring 已經(jīng)“手動”注冊了一些特殊的 Bean娘锁,如 environment莫秆、systemProperties 等悔详。

剩下的就是初始化 singleton beans 了茄螃,我們知道它們是單例的,如果沒有設(shè)置懶加載用狱,那么 Spring 會在接下來初始化所有的 singleton beans拼弃。

源碼位置:AbstractApplicationContext#finishBeanFactoryInitialization()

/**
     * Finish the initialization of this context's bean factory,
     * initializing all remaining singleton beans.
     */
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        // 初始化"conversionService"的bean,此接口用于類型之間的轉(zhuǎn)化溺忧,不是重點盯孙,放過我镀梭,自己去看
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        // 就是為了解析注解的值,沒啥重點
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return getEnvironment().resolvePlaceholders(strVal);
                }
            });
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        // 前面說過的埠偿,不是這里的重點
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            // 先初始化 LoadTimeWeaverAware 類型的bean
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        // 看方法名就知道了榜晦,凍結(jié)所有BeanDefinition的元數(shù)據(jù)了
        // 沒什么別的目的乾胶,因為到這一步的時候,Spring 已經(jīng)開始預(yù)初始化 singleton beans 了斩郎,
        // 肯定不希望這個時候還出現(xiàn) bean 定義解析缩宜、加載甥温、注冊姻蚓。
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        // 開始初始化,進來看吧乃秀,重點在里面
        beanFactory.preInstantiateSingletons();
    }

源碼位置:DefaultListableBeanFactory#preInstantiateSingletons()

    @Override
    public void preInstantiateSingletons() throws BeansException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Pre-instantiating singletons in " + this);
        }
        
        // copy 一個包含所有BeanName的集合副本
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

        // 觸發(fā)所有非懶加載的 singleton beans的初始化
        for (String beanName : beanNames) {
            // Bean 可能有繼承parent的關(guān)系跺讯,獲取合并后的RootBeanDefinition
            // 這個知識點用的很少的
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            
            // 非抽象殉农、非懶加載的singletons
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // FactoryBean知識點超凳,不了解的看另一篇文章或者自己google
                if (isFactoryBean(beanName)) {
                    // FactoryBean 會在 beanName前面加前綴"&"
                    final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                    boolean isEagerInit;
                    // SmartFactoryBean, 非重點轮傍,沒深入了解,放過我
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                            @Override
                            public Boolean run() {
                                return ((SmartFactoryBean<?>) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    // 正常的bean都到這里來了杭跪,重點哦涧尿,里面進行初始化了
                    // 調(diào)用鏈很復(fù)雜姑廉,單獨拉出來講,先繼續(xù)
                    getBean(beanName);
                }
            }
        }
        
        // 前面流程走完萌踱,說明所有的非懶加載singletonBean 完成了初始化

        // Trigger post-initialization callback for all applicable beans...
        // 看注釋就懂了号阿,如果我們定義的 bean 是實現(xiàn)了 SmartInitializingSingleton 接口的倦西,
        // 那么在這里得到回調(diào)赁严,忽略它吧疼约。
        for (String beanName : beanNames) {
            Object singletonInstance = getSingleton(beanName);
            if (singletonInstance instanceof SmartInitializingSingleton) {
                final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
                if (System.getSecurityManager() != null) {
                    AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        @Override
                        public Object run() {
                            smartSingleton.afterSingletonsInstantiated();
                            return null;
                        }
                    }, getAccessControlContext());
                }
                else {
                    smartSingleton.afterSingletonsInstantiated();
                }
            }
        }
    }

小知識點 關(guān)于 lazy-init

ApplicationContext 實現(xiàn)的默認行為就是在啟動時將所有 singleton bean提前進行實例化程剥。提前實例化意味著作為初始化過程的一部分织鲸,ApplicationContext 實例會創(chuàng)建并配置所有的singleton bean。通常情況下這是件好事稳诚,因為這樣在配置中的任何錯誤就會即刻被發(fā)現(xiàn)(否則的話可能要花幾個小時甚至幾天)瀑踢。

有時候這種默認處理可能并不是你想要的橱夭。如果你不想讓一個singleton bean 在ApplicationContext 實現(xiàn)在初始化時被提前實例化棘劣,那么可以將bean設(shè)置為延遲實例化。一個延遲初始化bean 將告訴IoC 容器是在啟動時還是在第一次被用到時實例化舆驶。

需要說明的是,如果一個bean被設(shè)置為延遲初始化拘荡,而另一個非延遲初始化的singleton bean 依賴于它珊皿,那么當ApplicationContext 提前實例化singleton bean時巨税,它必須也確保所有上述singleton 依賴bean也被預(yù)先初始化草添,當然也包括設(shè)置為延遲實例化的bean。因此抄淑,如果Ioc容器在啟動的時候創(chuàng)建了那些設(shè)置為延遲實例化的bean的實例肆资,你也不要覺得奇怪灶芝,因為那些延遲初始化的bean可能在配置的某個地方被注入到了一個非延遲初始化singleton bean里面夜涕。

結(jié)語

以上,本文就是關(guān)于Spring IoC 容器初始化的主要內(nèi)容栖秕。

Spring IoC 的設(shè)計中簇捍,Bean定義的解析和Bean的依賴注入俏拱,是兩個獨立的過程锅必,前面所有內(nèi)容講的就是IoC容器的初始化惕艳,資源定位远搪、載入以及解析BeanDefinition并且注冊谁鳍。

最后一步的實例化所有單例劫瞳,引入了getBean()方法志于,這就是Spring IoC 依賴注入的入口。也是下節(jié)源碼解讀的主要內(nèi)容养泡。

另外說一句澜掩,上面的源碼解析钥组,肯定不會是完備的程梦,只是提取了我認為重要的東西橘荠。

如有疏漏哥童,敬請諒解和自己查閱相關(guān)資料學習贮懈。如果錯誤,敬請指正各聘!

本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布躲因!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末大脉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琐驴,更是在濱河造成了極大的恐慌棍矛,老刑警劉巖抛杨,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怖现,死亡現(xiàn)場離奇詭異屈嗤,居然都是意外死亡饶号,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涩禀,“玉大人然眼,你說我怎么就攤上這事高每。” “怎么了爷怀?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵霉撵,是天一觀的道長徒坡。 經(jīng)常有香客問我,道長伦泥,這世上最難降的妖魔是什么不脯? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任防楷,我火速辦了婚禮复局,結(jié)果婚禮上粟判,老公的妹妹穿的比我還像新娘档礁。我一直安慰自己呻澜,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布平道。 她就那樣靜靜地躺著,像睡著了一般窘疮。 火紅的嫁衣襯著肌膚如雪闸衫。 梳的紋絲不亂的頭發(fā)上诽嘉,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天骄酗,我揣著相機與錄音,去河邊找鬼睛琳。 笑死师骗,一個胖子當著我的面吹牛讨惩,可吹牛的內(nèi)容都是我干的荐捻。 我是一名探鬼主播靴患,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼鸳君,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砸紊?” 一聲冷哼從身側(cè)響起醉顽,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤游添,失蹤者是張志新(化名)和其女友劉穎通熄,沒想到半個月后唇辨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赏枚,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年鞍帝,在試婚紗的時候發(fā)現(xiàn)自己被綠了帕涌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚓曼。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡纫版,死狀恐怖其弊,靈堂內(nèi)的尸體忽然破棺而出膀斋,到底是詐尸還是另有隱情仰担,我是刑警寧澤摔蓝,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布贮尉,位于F島的核電站,受9級特大地震影響败砂,放射性物質(zhì)發(fā)生泄漏吠卷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一路操、第九天 我趴在偏房一處隱蔽的房頂上張望屯仗。 院中可真熱鬧魁袜,春花似錦、人聲如沸店量。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怀伦。三九已至山林,卻和暖如春捌朴,著一層夾襖步出監(jiān)牢的瞬間砂蔽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工镣隶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留安岂,地道東北人域那。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓次员,卻偏偏與公主長得像淑蔚,于是被迫代替她去往敵國和親刹衫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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

  • 2.1 我們的理念是:讓別人為你服務(wù) IoC是隨著近年來輕量級容器(Lightweight Container)的...
    好好學習Sun閱讀 2,700評論 0 11
  • Spring容器高層視圖 Spring 啟動時讀取應(yīng)用程序提供的Bean配置信息带迟,并在Spring容器中生成一份相...
    Theriseof閱讀 2,796評論 1 24
  • 如果對IOC的概念還不是很清楚甥桂,可以先閱讀上一篇文章:Spring_IOC_01——概念講解 Spring IOC...
    _Zy閱讀 1,145評論 3 12
  • 之前就IOC的原理及使用有了一定的認識,作為Spring核心特性之一邮旷,IOC是很有必要重點掌握的黄选。這次沉淀將會開啟...
    天羽天閱讀 494評論 0 4
  • 前言 實際上我所有的博客都是原來對原來印象筆記里筆記內(nèi)容的加工,關(guān)于Spring源碼自己已經(jīng)解析了很多遍婶肩,但是時間...
    后廠村老司機閱讀 2,513評論 0 6