SpringIOC初始化過程學習

mocas_wang發表於2020-10-10

目錄

1 SpringIoC容器系列

2 初始化過程

2.1  Resource定位

2.2 BeanDefinition載入

2.3 註冊BeanDefition

3 流程分析


1 SpringIoC容器系列

(BeanFactory跟ApplicationContext)

BeanFactory跟ApplicationContext都屬於Spring容器。
BeanFactory:定義了最基本的容器設計規範,如getBean(),containBean(),getType()等基本的方法。

ApplicationContext:由圖可知ApplicationContext應用的上下文都是基於ConfigurableApplicationContext跟WebApplicationContext的實現。ApplicationContext繼承了BeanFactory的介面同時又繼承MessageSource(支援不同的資訊源),ResourceLoader(訪問資源),ApplicationEventPublisher(支援應用事件)。

                                      IoC介面設計圖

2 初始化過程

IoC容器的初始化包括三個過程:

  • 第一個過程是 Resource定位 過程。這個Resource定位指的是BeanDefinition的資源定位,它由ResourceLoader通過統一的Resource介面來完成,這個Resource對各種形式的BeanDefinition的使用都提供了統一介面。比如.在檔案系統中的Bean定義資訊可以使用FileSystemResource來進行抽象。在類路徑中的Bean定義資訊可以使用ClassPathResource來進行抽象等等。這個定位過程類似於容器尋找資料的過程,就像用水捅裝水先要把水找到一樣。

  • 第二個過程是 BeanDefinition的載入 。這個載入過程是把使用者定義好的Bean表示成IoC容器內部的資料結構,而這個容器內部的資料結構就是BeanDefinition。下面介紹這個資料結構的詳細定義。具體來說,這個BeanDefinition實際上就是POJO物件在IoC容器中的抽象。通過這個BeanDefinition定義的資料結構,使IoC容器能夠方便地對Polo物件也就是Bean進行管理。

  • 第三個過程是 向IoC容器註冊這些Bean Definition 的過程。這個過程是通過呼叫BeanDefinitionRegistry介面的實現來完成的。這個註冊過程把載人過程中解析得到的BeanDeftnition向IoC容器進行註冊。在IoC容器內部將BeanDefinition注人到一個HashMap中去,IoC容器就是通過這個HashMap來持有這些BeanDefinition資料的

上面提到的過程一般是不包括Bean的依賴注入的實現。在Spring中,Bean的載入和依賴注入是兩個獨立的過程。依賴注入一般發生在應用第一次通過getBean向容器索取Bean的時候。下面的一張圖描述了這三個過程呼叫的主要方法,圖中的四個過程其實描述的是上面的第二個過程和第三個過程:

再細分下去17個小模組

1.尋找入口——>2.獲取配置檔案路徑——>3.容器開始啟動——>4.開始解析配置檔案路徑——>5.載入配置路徑——>6.分配路徑處理策略——>7.解析配置檔案路徑——>8.載入配置檔案資訊——>9.開始解析配置檔案——>10.分配解析配置檔案策略——>11.將配置檔案載入到記憶體——>12.載入<bean>元素——>13.載入<property>元素——>14.載入<property>子元素——>15.載入<list>子元素——>16.分配註冊策略——>17.開始向容器註冊

圖片中1-14可劃歸到定位,15-24載入,25-26註冊

2.1  Resource定位

  • 定位:通過Resource定位BeanDefinition,BeanDefinition抽象了對bean的定義,比如bean的資訊,依賴關係等。這個過程可以想象成尋找bean的過程。

下面我們通過程式碼來簡要分析下容器是如何初始化的

public class ApplicationContextInit {

    public static void main(String[] args) {
        FileSystemXmlApplicationContext fileSystemXmlApplicationContext = new FileSystemXmlApplicationContext("bean.xml");
    }
}

FileSystemXmlApplicationContext是通過檔案來載入Resource的,執行上述程式碼

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
    //...省略部分原始碼
    //通過檔案的位置來定位到beanDefinition
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }
    
    //通過refresh()方法來完成BeanDefinition資訊讀取和載入
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

}

refresh()來啟動IoC容器的初始化

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 容器初始化的一些準備工作.
            prepareRefresh();

            // 告知子類要初始化BeanFactory,BeanDefinition資訊的讀取是在子類的
            // refreshBeanFactory()方法裡完成的
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // 準備bean工廠以在此上下文中使用。
            prepareBeanFactory(beanFactory);

            try {
                // 設定beanFactory的後置處理
                postProcessBeanFactory(beanFactory);
                // 呼叫beanFactory的後置處理
                invokeBeanFactoryPostProcessors(beanFactory);
                // 註冊beanFactory的後置處理
                registerBeanPostProcessors(beanFactory);
                // 初始化上下文的訊息
                initMessageSource();
                // 初始化上下的事件
                initApplicationEventMulticaster();
                // 初始化一些特殊的bean
                onRefresh();
                // 檢查一些監聽的bean並註冊到容器中
                registerListeners();
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
                // 釋出容器事件,結束Refresh過程
                finishRefresh();
            }
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // 銷燬已經生成的bean
                destroyBeans();
                // 重置啟用狀態.
                cancelRefresh(ex);
                throw ex;
            }

            finally {
            
                resetCommonCaches();
            }
        }
    }

beanDefinition資訊是通過ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()裡的refreshBeanFactory()來完成的,而這個方法則是在AbstractRefreshableApplicationContext實現的。

@Override
    protected final void refreshBeanFactory() throws BeansException {
            //如果容器已經存在,那麼銷燬並且關閉該容器,保證每次產生的都是新的容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
                   //建立基礎的BeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
                   //載入BeanDefinition的資訊
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

DefaultListableBeanFactory其實是一個最基礎的容器,很多容器都是基於這個容器來作擴充套件,那麼這個容器裡自然也包含了很多基礎重要的功能,那麼通過loadBeanDefinitions()來完成BeanDefinition資訊的載入的,這裡是委託子類來完成這個工作的。

//抽象類,具體的resource定位跟BeanDefinition的載入是委託子類來完成的
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException;


//這是loadBeanDefinitions的具體實現
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//建立XmlBeanDefinitionReader,並通過回撥設定到beanFactory裡面去
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// 使用此上下文的資源載入環境配置beanFactory
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//啟動bean資訊的載入過程
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }
//這裡應該算是容器初始化第一步resource定位,首先得到beanDefinition資訊的Resource定位
//然後通過XmlBeanDefinitionReader來讀取
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//通過resource來定位
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
//通過檔案路徑來定位
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }


public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//獲取ResourceLoader
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }
              //判斷ResourceLoader的路徑模式,
        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

getResourceByPath被子類FileSystemXmlApplicationContext實現,最終返回FileSystemResource物件,通過這個物件,Spring進行相關的I/O操作,完成beanDefinition的定位。
總結下Resource定位BeanDefinition的流程
1.FileSystemXmlApplicationContext裡呼叫refresh()方法初始化IoC容器。
2.在refresh()方法裡呼叫obtainFreshBeanFactory()裡面的refreshBeanFactory()來完成BeanDefinition的定位,而refreshBeanFactory()是由子類AbstractRefreshableApplicationContext來實現的。
3.refreshBeanFactory()中是通過loadBeanDefinitions()來完成BeanDefinition的定位,而loadBeanDefinitions()是一個抽象的方法,具體由AbstractBeanDefinitionReader裡的loadBeanDefinitions()來實現。
4.在loadBeanDefinitions()通過DefaultResourceLoader的getResource方法裡返回resource物件。

2.2 BeanDefinition載入

  • 載入:BeanDefinition的資訊已經定位到了,第二步就是把定義的BeanDefinition在Ioc容器中轉化成一個Spring內部標示的資料結構的過程。

1.什麼是BeanDefinition? BeanDefinition與Resource的聯絡呢?

/**
     * Load bean definitions from the specified resource.
     * @param resource the resource descriptor
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

 

  總之,BeanDefinition相當於一個資料結構,這個資料結構的生成過程是根據定位的resource資源物件中的bean而來的,這些bean在Spirng IoC容器內部表示成了的BeanDefintion這樣的資料結構,IoC容器對bean的管理和依賴注入的實現都是通過操作BeanDefinition來進行的。

2.如何將BeanDefinition載入到容器?
  在Spring中配置檔案主要格式是XML,對於用來讀取XML型資原始檔來進行初始化的IoC 容器而言,該類容器會使用到AbstractXmlApplicationContext類,該類定義了一個名為loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的方法用於獲取BeanDefinition:

// 該方法屬於AbstractXmlApplicationContect類
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    this.initBeanDefinitionReader(beanDefinitionReader);
    // 用於獲取BeanDefinition
    this.loadBeanDefinitions(beanDefinitionReader);
}

此方法在具體執行過程中首先會new一個與容器對應的BeanDefinitionReader型例項物件,然後將生成的BeanDefintionReader例項作為引數傳入loadBeanDefintions(XmlBeanDefinitionReader),繼續往下執行載入BeanDefintion的過程。例如AbstractXmlApplicationContext有兩個實現類:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext,這些容器在呼叫此方法時會建立一個XmlBeanDefinitionReader類物件專門用來載入所有的BeanDefinition。

下面以XmlBeanDefinitionReader物件載入BeanDefinition為例,使用原始碼說明載入BeanDefinition的過程:

// 該方法屬於AbstractXmlApplicationContect類protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();//獲取所有定位到的resource資源位置(使用者定義)
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);//載入resources
        }
        String[] configLocations = getConfigLocations();//獲取所有本地配置檔案的位置(容器自身)
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);//載入resources
        }
}

通過上面程式碼將使用者定義的資源以及容器本身需要的資源全部載入到reader中,reader.loadBeanDefinitions方法的原始碼如下:

// 該方法屬於AbstractBeanDefinitionReader類, 父介面BeanDefinitionReader
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    for (Resource resource : resources) {
        // 將所有資源全部載入,交給AbstractBeanDefinitionReader的實現子類處理這些resource
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}

BeanDefinitionReader介面定義了 int loadBeanDefinitions(Resource resource)方法:

int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

XmlBeanDefinitionReader 類實現了BeanDefinitionReader介面中的loadBeanDefinitions(Resource)方法,其繼承關係如上圖所示。XmlBeanDefinitionReader類中幾主要對載入的所有resource開始進行處理,大致過程是,先將resource包裝為EncodeResource型別,然後處理,為生成BeanDefinition物件做準備,其主要幾個方法的原始碼如下:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        // 包裝resource為EncodeResource型別
        return loadBeanDefinitions(new EncodedResource(resource)); 
    }

    // 載入包裝後的EncodeResource資源
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        try {
            // 通過resource物件得到XML檔案內容輸入流,併為IO的InputSource做準備
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                // Create a new input source with a byte stream.
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // 開始準備 load bean definitions from the specified XML file
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
    }

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            // 獲取指定資源的驗證模式
            int validationMode = getValidationModeForResource(resource);

            // 從資源物件中載入DocumentL物件,大致過程為:將resource資原始檔的內容讀入到document中
            // DocumentLoader在容器讀取XML檔案過程中有著舉足輕重的作用!
            // XmlBeanDefinitionReader例項化時會建立一個DefaultDocumentLoader型的私有屬性,繼而呼叫loadDocument方法
            // inputSource--要載入的文件的輸入源
            Document doc = this.documentLoader.loadDocument(
                    inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
            
            // 將document檔案的bean封裝成BeanDefinition,並註冊到容器
            return registerBeanDefinitions(doc, resource);
        }
        catch ...(略)
    }

parseBeanDefinitionElement(Element ele)方法會呼叫parseBeanDefinitionElement(ele, null)方法,並將值返回BeanDefinitionHolder類物件,這個方法將會對給定的<bean>標籤進行解析,如果在解析<bean>標籤的過程中出現錯誤則返回null。

需要強調一下的是parseBeanDefinitionElement(ele, null)方法中產生了一個抽象型別的BeanDefinition例項,這也是我們首次看到直接定義BeanDefinition的地方,這個方法裡面會將<bean>標籤中的內容解析到BeanDefinition中,之後再對BeanDefinition進行包裝,將它與beanName,Alias等封裝到BeanDefinitionHolder 物件中,該部分原始碼如下:

2.3 註冊BeanDefition

註冊:將抽象好的BeanDefinition統一註冊到IoC容器中,IoC容器是通過hashMap來維護BeanDefinition資訊的,key為beanName,value為BeanDefinition。

 最終Bean配置會被解析成BeanDefinition並與beanName,Alias一同封裝到BeanDefinitionHolder類中, 之後beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()),註冊到DefaultListableBeanFactory.beanDefinitionMap中。之後客戶端如果要獲取Bean物件,Spring容器會根據註冊的BeanDefinition資訊進行例項化。

完成了上面的三步後,目前ApplicationContext中有兩種型別的結構,一個是DefaultListableBeanFactory,它是Spring IOC容器,另一種是若干個BeanDefinitionHolder,這裡麵包含實際的Bean物件,AbstractBeanDefition。

需要把二者關聯起來,這樣Spring才能對Bean進行管理。在DefaultListableBeanFactory中定義了一個Map物件,儲存所有的BeanDefition。這個註冊的過程就是把前面解析得到的Bean放入這個Map的過程。

3 流程分析

1、專案從 ApplicationContext app = new ClassPathXmlApplicationContext(“applicationContext.xml”);
2、找到工廠實現類【ClassPathXmlApplicationContext】中對應的構造方法,執行ioc容器初始化:
如下:

*使用給定的父類建立一個新的ClassPathXmlApplicationContext,
從給定的XML檔案中載入定義。
* @param configLocations資源位置陣列
是否自動重新整理上下文,
載入所有bean定義並建立所有的單例。
*或者,在進一步配置上下文之後手動呼叫refresh。
* @param父上下文
如果上下文建立失敗,@丟擲BeansException
* @see # refresh ()

3、找到工廠抽象父類【AbstractApplicationContext】中的【refresh】方法:
        3.1.該方法實現解析xml配置檔案內容,封裝成BeanDefinition物件,註冊到BeanFactory中
        3.2.該方法實現一些基礎元件的註冊:bean後置處理器元件、監聽器元件、國際化資源元件
        3.3.該方法實現bean物件的真正例項化。細節:初始化全部【singleton】單例物件,標記為【lazy-init】延遲載入的物件除外

流程小結:
1、在應用程式中初始化ioc容器的入口是 ClassPathXmlApplicationContext工廠實現類
2、在ClassPathXmlApplicationContext的構造方法中呼叫了refresh方法
    2.1.refresh不僅僅是初始化ioc容器,如果已經有ioc容器了就更新容器
    2.2.spring框架在處理過程中會考慮先釋放已經存在的ioc容器。再重新建立一個ioc容器
3、spring框架允許在一個應用中有多個ioc容器,他們之間是父子關係。ssm框架就有兩個ioc容器:
    3.1通過ContextLoaderListener監聽器,載入spring配置檔案,建立的父容器
    3.2通過DispatcherServlet前端控制器載入springmvc主配置檔案建立的子容器
4、spring框架在建立ioc容器時,主體流程:
    4.1設定容器的初始化狀態,如:容器的啟動時間,容器的啟用狀態
    4.2解析bean.xml配置檔案,將配置檔案中的資訊解析封裝程BeanDefinition物件
    4.3將BeanDefinition物件註冊到BeanFactory容器中。此時還沒有真正建立bean物件,只是解析封裝xml配置檔案的內容
    4.4設定一些公共資源。如:bean的後置處理器,類載入器,監聽器,國際化資源等
    4.5根據BeanDefinition物件真正建立bean物件, 此時建立的全是單例【singleton】,並且不是延遲載入【lazy-init】的物件
    4.6最後一步廣播事件,進行善後處理

CustomerController

/**
 * 客戶表現層
 */
public class CustomerController {
    public static void main(String[] args) {
        // 1.載入spring 配置檔案,初始化建立ioc容器
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
        // 2.從ioc容器獲取service
        CustomerService customerService = (CustomerService)context.getBean("customerService");
        // 3.儲存客戶操作
        customerService.saveCustomer();
    }
}

ClassPathXmlApplicationContext

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {        // 資源配置檔案成員變數,是一個陣列,支援多個spring的配置檔案
     @Nullable
    private Resource[] configResources;
     // 預設構造方法
    public ClassPathXmlApplicationContext() {
    }
    // 如果已經存在一個ioc容器,可以在構造的時候設定【父】容器
    public ClassPathXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }
     // 【重點跟蹤】根據xxx.xml配置檔案,建立ioc容器
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[]{configLocation}, true, (ApplicationContext)null);
}
..........................................
    /**
    *【重點跟蹤】方法說明:
    *    根據xml檔案的定義,以及父容器,建立一個新的ClassPathXmlApplicationContext
    *
    *引數說明:
    *    configLocations:xml配置檔案陣列
    *    refresh:是否要重新建立ioc容器。載入全部bean的定義和建立所有的單例物件
    *    parent:父容器
    */
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);// 設定父容器
       // 根據提供的路徑,處理成配置檔案陣列(以分號、逗號、空格、tab、換行符分割)
        this.setConfigLocations(configLocations);
        if (refresh) {
            this.refresh();// 【核心方法】:該方法表示初始化(或者重建)ioc容器。即可以把原來的ApplicationContext銷燬,重新執行初始化建立
        }
    }
..........................................
}

AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {   
 ..........................................
     /**
     *【重點跟蹤】方法說明:
     *    【核心方法】:該方法表示初始化(或者重建)ioc容器。即可以把原來的ApplicationContext銷燬,重新執行初始化建立 
     */
    public void refresh() throws BeansException, IllegalStateException {
       // 建立ioc容器,同步加鎖,保障執行緒安全
       synchronized (this.startupShutdownMonitor) {
            // 準備工作:記錄容器啟動的時間,和狀態標記
            prepareRefresh();
            // 關鍵步驟:
                //    1.根據配置檔案中配置內容,解析成一個個Bean例項(BeanDefinition)
             //    2.將一個個Bean例項,註冊到BeanFactory中
                // 3.細節:這裡的Bean例項僅僅是描述Bean的相關資訊,此時還沒有真正建立對應的bean物件
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 設定BeanFactory:
                // 1.設定類載入器
                // 2.設定BeanPostProcessor(bean後置處理器)
                // 3.註冊特殊的bean(框架內部使用的bean)
            prepareBeanFactory(beanFactory);
            try {
                // 設定BeanFactoryPostProcessor
                postProcessBeanFactory(beanFactory);
                // 呼叫BeanFactoryPostProcessor各個實現類的 postProcessBeanFactory(factory) 方法
                 // 與上一步合起來,可以理解為是給Bean提供的一種擴充套件機制。比如可以讓我們的Bean實現BeanFactoryPostProcessor介面,增強該Bean的功能
                invokeBeanFactoryPostProcessors(beanFactory);
                // 註冊BeanPostProcessor的實現類:
                  // 1.該介面有兩個方法:
                  //      postProcessBeforeInitialization(),在init-method屬性指定的方法前呼叫
                  //      postProcessAfterInitialization(),在init-method屬性指定的方法後呼叫
                registerBeanPostProcessors(beanFactory);
                // 初始化國際化支援的資原始檔
                initMessageSource();
                // 初始化ApplicationContext事件廣播器
                initApplicationEventMulticaster();
                // 模板方法:用於特殊bean的初始化,預設是空實現(在api中如果預留了一些方法實現是空,表示該方法是留給子類自我實現。那麼這些方法稱為:鉤子方法)
                onRefresh();
                // 註冊事件監聽器:監聽器需要實現ApplicationListener介面
                registerListeners();
                  // 【重點步驟】:
                  // 1.例項化所有單例bean物件,除開延遲載入的bean
                  // <bean id="customerDao" class="com.itheima.dao.impl.CustomerDaoImpl" lazy-init="false" scope="singleton"/>
                finishBeanFactoryInitialization(beanFactory);
                // 【最後一步】:
                  // 1.釋出廣播事件。ApplicationContext初始化完成
                finishRefresh();
            }catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
                 // 如果發生異常,需要銷燬已經建立的singleton物件
                destroyBeans();
                  // 將active狀態設定為false
                cancelRefresh(ex);
                // Propagate exception to caller.
                throw ex;
            }finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
     
 ..........................................   
}

 

 

 

 

 

相關文章