1. 程式人生 > >Spring Ioc 容器如何工作

Spring Ioc 容器如何工作

Ioc 容器如何工作

前面介紹了 Core 元件、Bean 元件和 Context 元件的結構與相互關係,下面這裡從使用者角度看一下他們是如何執行的,以及我們如何讓 Spring 完成各種功能,Spring 到底能有那些功能,這些功能是如何得來的,下面介紹。

如何建立 BeanFactory 工廠

正如圖 2 描述的那樣,Ioc 容器實際上就是 Context 元件結合其他兩個元件共同構建了一個 Bean 關係網,如何構建這個關係網?構建的入口就在 AbstractApplicationContext 類的 refresh 方法中。這個方法的程式碼如下:

清單 . AbstractApplicationContext.refresh

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // 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 (BeansException ex) {
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();
            // Reset 'active' flag.
            cancelRefresh(ex);
            // Propagate exception to caller.
            throw ex;
        }
    }
}
這個方法就是構建整個 Ioc 容器過程的完整的程式碼,瞭解了裡面的每一行程式碼基本上就瞭解大部分 Spring 的原理和功能了。

這段程式碼主要包含這樣幾個步驟:

  • 構建 BeanFactory,以便於產生所需的“演員”
  • 註冊可能感興趣的事件
  • 建立 Bean 例項物件
  • 觸發被監聽的事件

下面就結合程式碼分析這幾個過程。

第二三句就是在建立和配置 BeanFactory。這裡是 refresh 也就是重新整理配置,前面介紹了 Context 有可更新的子類,這裡正是實現這個功能,當 BeanFactory 已存在是就更新,如果沒有就新建立。下面是更新 BeanFactory 的方法程式碼:

清單 . AbstractRefreshableApplicationContext. refreshBeanFactory

protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(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);
    }
}
這個方法實現了 AbstractApplicationContext 的抽象方法 refreshBeanFactory,這段程式碼清楚的說明了 BeanFactory 的建立過程。注意 BeanFactory 物件的型別的變化,前面介紹了他有很多子類,在什麼情況下使用不同的子類這非常關鍵。BeanFactory 的原始物件是 DefaultListableBeanFactory,這個非常關鍵,因為他設計到後面對這個物件的多種操作,下面看一下這個類的繼承層次類圖:

圖 . DefaultListableBeanFactory 類繼承關係圖

https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/origin_image009.png

從這個圖中發現除了 BeanFactory 相關的類外,還發現了與 Bean 的 register 相關。這在 refreshBeanFactory 方法中有一行 loadBeanDefinitions(beanFactory) 將找到答案,這個方法將開始載入、解析 Bean 的定義,也就是把使用者定義的資料結構轉化為 Ioc 容器中的特定資料結構。

這個過程可以用下面時序圖解釋:

圖 . 建立 BeanFactory 時序圖

https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/origin_image010.png

Bean 的解析和登記流程時序圖如下:

圖 . 解析和登記 Bean 物件時序圖

https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/origin_image011.png

建立好 BeanFactory 後,接下去新增一些 Spring 本身需要的一些工具類,這個操作在 AbstractApplicationContext 的 prepareBeanFactory 方法完成。

AbstractApplicationContext 中接下來的三行程式碼對 Spring 的功能擴充套件性起了至關重要的作用。前兩行主要是讓你現在可以對已經構建的 BeanFactory 的配置做修改,後面一行就是讓你可以對以後再建立 Bean 的例項物件時新增一些自定義的操作。所以他們都是擴充套件了 Spring 的功能,所以我們要學習使用 Spring 必須對這一部分搞清楚。

其中在 invokeBeanFactoryPostProcessors 方法中主要是獲取實現 BeanFactoryPostProcessor 介面的子類。並執行它的 postProcessBeanFactory 方法,這個方法的宣告如下:

清單 . BeanFactoryPostProcessor.postProcessBeanFactory

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
    throws BeansException;
它的引數是 beanFactory,說明可以對 beanFactory 做修改,這裡注意這個 beanFactory 是 ConfigurableListableBeanFactory 型別的,這也印證了前面介紹的不同 BeanFactory 所使用的場合不同,這裡只能是可配置的 BeanFactory,防止一些資料被使用者隨意修改。

registerBeanPostProcessors 方法也是可以獲取使用者定義的實現了 BeanPostProcessor 介面的子類,並執行把它們註冊到 BeanFactory 物件中的 beanPostProcessors 變數中。BeanPostProcessor 中聲明瞭兩個方法:postProcessBeforeInitialization、postProcessAfterInitialization 分別用於在 Bean 物件初始化時執行。可以執行使用者自定義的操作。

後面的幾行程式碼是初始化監聽事件和對系統的其他監聽者的註冊,監聽者必須是 ApplicationListener 的子類。

如何建立 Bean 例項並構建 Bean 的關係網

下面就是 Bean 的例項化程式碼,是從 finishBeanFactoryInitialization 方法開始的。

清單 . AbstractApplicationContext.finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(
    ConfigurableListableBeanFactory beanFactory) {
 
    // Stop using the temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(null);
 
    // Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();
 
    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}
從上面程式碼中可以發現 Bean 的例項化是在 BeanFactory 中發生的。preInstantiateSingletons 方法的程式碼如下:

清單 . DefaultListableBeanFactory.preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
    if (this.logger.isInfoEnabled()) {
        this.logger.info("Pre-instantiating singletons in " + this);
    }
    synchronized (this.beanDefinitionMap) {
        for (String beanName : this.beanDefinitionNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton()
                && !bd.isLazyInit()) {
                if (isFactoryBean(beanName)) {
                    final FactoryBean factory =
                        (FactoryBean) getBean(FACTORY_BEAN_PREFIX+ beanName);
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null
                        && factory instanceof SmartFactoryBean) {
                        isEagerInit = AccessController.doPrivileged(
                            new PrivilegedAction<Boolean>() {
                            public Boolean run() {
                                return ((SmartFactoryBean) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    else {
                        isEagerInit = factory instanceof SmartFactoryBean
                            && ((SmartFactoryBean) factory).isEagerInit();
                    }
                    if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                else {
                    getBean(beanName);
                }
            }
        }
    }
}
這裡出現了一個非常重要的 Bean —— FactoryBean,可以說 Spring 一大半的擴充套件的功能都與這個 Bean 有關,這是個特殊的 Bean 他是個工廠 Bean,可以產生 Bean 的 Bean,這裡的產生 Bean 是指 Bean 的例項,如果一個類繼承 FactoryBean 使用者可以自己定義產生例項物件的方法只要實現他的 getObject 方法。然而在 Spring 內部這個 Bean 的例項物件是 FactoryBean,通過呼叫這個物件的 getObject 方法就能獲取使用者自定義產生的物件,從而為 Spring 提供了很好的擴充套件性。Spring 獲取 FactoryBean 本身的物件是在前面加上 & 來完成的。

如何建立 Bean 的例項物件以及如何構建 Bean 例項物件之間的關聯關係式 Spring 中的一個核心關鍵,下面是這個過程的流程圖。

圖 .Bean 例項建立流程圖

https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/origin_image012.gif

如果是普通的 Bean 就直接建立他的例項,是通過呼叫 getBean 方法。下面是建立 Bean 例項的時序圖:

圖 .Bean 例項建立時序圖

https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/origin_image013.png

還有一個非常重要的部分就是建立 Bean 物件例項之間的關係,這也是 Spring 框架的核心競爭力,何時、如何建立他們之間的關係請看下面的時序圖:

圖 .Bean 物件關係建立

https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/origin_image014.png

Ioc 容器的擴充套件點

現在還有一個問題就是如何讓這些 Bean 物件有一定的擴充套件性,就是可以加入使用者的一些操作。那麼有哪些擴充套件點呢? Spring 又是如何呼叫到這些擴充套件點的?

對 Spring 的 Ioc 容器來說,主要有這麼幾個。BeanFactoryPostProcessor, BeanPostProcessor。他們分別是在構建 BeanFactory 和構建 Bean 物件時呼叫。還有就是 InitializingBean 和 DisposableBean 他們分別是在 Bean 例項建立和銷燬時被呼叫。使用者可以實現這些介面中定義的方法,Spring 就會在適當的時候呼叫他們。還有一個是 FactoryBean 他是個特殊的 Bean,這個 Bean 可以被使用者更多的控制。

這些擴充套件點通常也是我們使用 Spring 來完成我們特定任務的地方,如何精通 Spring 就看你有沒有掌握好 Spring 有哪些擴充套件點,並且如何使用他們,要知道如何使用他們就必須瞭解他們內在的機理。可以用下面一個比喻來解釋。

我們把 Ioc 容器比作一個箱子,這個箱子裡有若干個球的模子,可以用這些模子來造很多種不同的球,還有一個造這些球模的機器,這個機器可以產生球模。那麼他們的對應關係就是 BeanFactory 就是那個造球模的機器,球模就是 Bean,而球模造出來的球就是 Bean 的例項。那前面所說的幾個擴充套件點又在什麼地方呢? BeanFactoryPostProcessor 對應到當造球模被造出來時,你將有機會可以對其做出設當的修正,也就是他可以幫你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的開始和結束階段,你可以完成一些預備和掃尾工作。BeanPostProcessor 就可以讓你對球模造出來的球做出適當的修正。最後還有一個 FactoryBean,它可是一個神奇的球模。這個球模不是預先就定型了,而是由你來給他確定它的形狀,既然你可以確定這個球模型的形狀,當然他造出來的球肯定就是你想要的球了,這樣在這個箱子里尼可以發現所有你想要的球

Ioc 容器如何為我所用

前面的介紹了 Spring 容器的構建過程,那 Spring 能為我們做什麼,Spring 的 Ioc 容器又能做什麼呢?我們使用 Spring 必須要首先構建 Ioc 容器,沒有它 Spring 無法工作,ApplicatonContext.xml 就是 Ioc 容器的預設配置檔案,Spring 的所有特性功能都是基於這個 Ioc 容器工作的,比如後面要介紹的 AOP。

Ioc 它實際上就是為你構建了一個魔方,Spring 為你搭好了骨骼架構,這個魔方到底能變出什麼好的東西出來,這必須要有你的參與。那我們怎麼參與?這就是前面說的要了解 Spring 中那有些擴充套件點,我們通過實現那些擴充套件點來改變 Spring 的通用行為。至於如何實現擴充套件點來得到我們想要的個性結果,Spring 中有很多例子,其中 AOP 的實現就是 Spring 本身實現了其擴充套件點來達到了它想要的特性功能,可以拿來參考。



相關推薦

Spring Ioc 容器如何工作

Ioc 容器如何工作 前面介紹了 Core 元件、Bean 元件和 Context 元件的結構與相互關係,下面這裡從使用者角度看一下他們是如何執行的,以及我們如何讓 Spring 完成各種功能,Spring 到底能有那些功能,這些功能是如何得來的,下面介紹。 如何建立 B

spring IoC容器的實現。

管理 體系 控制 我們 管理系 content 具體實現 抽象 目的 控制反轉是spring的重要概念。而實現控制反轉的IoC容器具體又是如何實現呢。 IoC容器的目的是能夠管理系統中各個對象之間的關系和依賴,為了實現這個功能,spring框架對Bean做了進一步抽象 Be

Spring IoC容器管理Action

framework 實現類 ces 攔截 ons servlet 需要 所有 使用 Spring IoC容器管理Action有兩種方式:DelegatingRequestProcessor、DelegatingActionProxy 不管采用哪一種方式,都需要隨應用啟動時創

Spring IOC容器的初始化-(三)BeanDefinition的註冊

store erro pan customize 註冊 failed mono def override ---恢復內容開始--- 前言 在上一篇中有一處代碼是BeanDefiniton註冊的入口,我們回顧一下。 1.BeanDefiniton在IOC容器註冊 首先我

Spring IOC容器的基本應用

信息 ioc容器 ans control gpo getbean 配置文件 如何工作 簡單 Spring IOC概述   IOC全稱Inversion of Control,被譯為控制反轉,是指程序中對象的獲取方式發生反轉,由最初的new方式創建,轉變為由第三方框架創

Spring IOC 容器源碼分析 - 創建單例 bean 的過程

event version trac 方法 del lB ctu prepare 先來 1. 簡介 在上一篇文章中,我比較詳細的分析了獲取 bean 的方法,也就是getBean(String)的實現邏輯。對於已實例化好的單例 bean,getBean(String) 方法

Spring IOC 容器源碼分析 - 創建原始 bean 對象

設置 assign 循環 處理器 from index boolean sar 興趣 1. 簡介 本篇文章是上一篇文章(創建單例 bean 的過程)的延續。在上一篇文章中,我們從戰略層面上領略了doCreateBean方法的全過程。本篇文章,我們就從戰術的層面上,詳細分析d

Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象

interface let 源碼分析 添加 eat object determine 方法調用 集合類 1. 簡介 本篇文章,我們來一起了解一下 Spring 是如何將配置文件中的屬性值填充到 bean 對象中的。我在前面幾篇文章中介紹過 Spring 創建 bean 的流

spring IOC容器的擴展

ESS 需要 getenv 級別 efault 監聽 roc val enc 在此之前已經完成了IOC對xml的解析和實例化工作,接下來需要分析Spring的高級版本對IOC容器的功能擴展: 代碼分析如下: synchronized (this.startupShutdo

03.Spring IoC 容器 - 初始化

itl ret num servlet fontsize eat 圖片 number sources 基本概念 Spring IoC 容器的初始化過程在監聽器 ContextLoaderListener 類中定義。 具體由該類的的 configureAndRefreshWe

Spring IoC容器實現

127.0.0.1 兩種 [] 作用 隱式調用 ini 告訴 而是 lee 1,Spring的兩種IoC容器 BeanFactory 基礎類型的IoC容器; 采用延遲初始化策略(容器初始化完成後並不會創建bean的對象,只有當收到初始化請求時才進行初始化); 由於延遲初

關於Spring IOC容器

版權 spm size align cto ccf 文件絕對路徑 推薦 sse 註:都是從net的各個角落找到的,如涉及到版權請聯系我,君並無意冒犯。 1,spring容器的三種加載方式: (1)FileSystemXmlApplicationContext這個方法是從文件

1.3淺談Spring(IOC容器的實現)

tap 就是 parser pojo file abstract throw cdd moni 這一節我們來討論IOC容器到底做了什麽。 還是借用之前的那段代碼 ClassPathXmlApplicationContext app = new ClassPathXmlAp

SpringFramework的核心:IOC容器的實現------Spring IOC容器概述

        之前在剛開始學習的時候,我曾經嘗試過研究《Spring技術內幕》這本書,但是由於當時的經驗以及知識儲備不足。導致我沒有很好地完成閱讀這本書,這本書是對於spring原始碼的很好的分析。所以現在在我已經學完spring之後我想回顧一下,重新審

Spring入門篇——第2章 Spring IOC容器

第2章 Spring IOC容器 介紹Spring IOC容器的基本概念和應用 2-1 IOC及Bean容器               在IO

ssm(2-4)Spring IOC 容器對 Bean 的生命週期管理執行順序

1.測試如下註解或屬性對Spring IOC 容器的生命週期的管理 @Bean(value="p1",destroyMethod = "destroy",initMethod = "init") <bean id="car" class="spring5.Car" init-method="init

05 Spring IOC 容器

1. Spring IOC 容器 2. Spring IOC 容器中如何管理物件 3. Spring Bean 容器初始化 4. Spring 注入 4.1 屬性注入 4.2 構造注入 1. Sp

Spring IoC 容器的設計與實現原理

上一篇文章講解的是IOC的原理,這一篇文章主要講解Spring IoC 容器的設計與實現原理   1.spring的IOC容器 在 Spring IoC 容器的設計中,容器有兩個系列,可以看成是容器的具體表現形式: BeanFactory 簡單容器:實現了容器的基本

Spring IoC容器的初始化

文章目錄 Resource定位 通過返回的resource物件,進行BeanDefinition的載入 將BeanDefiniton註冊到容器中 小結 關於Spirng IoC容器的初始化過程在《Spirng技術內

Spring IoC容器設計原理及高階特性

文章目錄 Spring IoC容器概述 IoC容器系列的設計與實現:BeanFactory和ApplicationContext BeanFactory BeanFactory容器的設計原理 Applicatio