您好,登錄后才能下訂單哦!
兩種實現(xiàn): 依賴查找(DL)和依賴注入(DI)。
IOC 和 DI 、DL 的關(guān)系(這個 DL,Avalon 和 EJB 就是使用的這種方式實現(xiàn)的 IoC):
DL 已經(jīng)被拋棄,因為他需要用戶自己去是使用 API 進行查找資源和組裝對象。即有侵入性。
注意:Java 使用 DI 方式實現(xiàn) IoC 的不止 Spring,包括 Google 的 Guice,還有一個冷門的 PicoContainer(極度輕量,但只提供 IoC)。
Spring 的 IoC 設(shè)計支持以下功能:
其中,最重要的就是依賴注入,從 XML 的配置上說, 即 ref 標簽。對應(yīng) Spring RuntimeBeanReference 對象。
對于 IoC 來說,最重要的就是容器。容器管理著 Bean 的生命周期,控制著 Bean 的依賴注入。
那么, Spring 如何設(shè)計容器的呢?
Spring 作者 Rod Johnson 設(shè)計了兩個接口用以表示容器。
BeanFactory 粗暴簡單,可以理解為就是個 HashMap,Key 是 BeanName,Value 是 Bean 實例。通常只提供注冊(put),獲?。╣et)這兩個功能。我們可以稱之為 “低級容器”。
ApplicationContext 可以稱之為 “高級容器”。因為他比 BeanFactory 多了更多的功能。他繼承了多個接口。因此具備了更多的功能。
例如資源的獲取,支持多種消息(例如 JSP tag 的支持),對 BeanFactory 多了工具級別的支持等待。所以你看他的名字,已經(jīng)不是 BeanFactory 之類的工廠了,而是 “應(yīng)用上下文”, 代表著整個大容器的所有功能。
該接口定義了一個 refresh 方法,此方法是所有閱讀 Spring 源碼的人的最熟悉的方法,用于刷新整個容器,即重新加載/刷新所有的 bean。
當然,除了這兩個大接口,還有其他的輔助接口,但我今天不會花太多篇幅介紹他們。
為了更直觀的展示 “低級容器” 和 “高級容器” 的關(guān)系,我這里通過常用的 ClassPathXmlApplicationContext 類,來展示整個容器的層級 UML 關(guān)系。
有點復雜? 先不要慌,我來解釋一下。
最上面的 BeanFactory 知道吧?我就不講了。
下面的 3 個綠色的,都是功能擴展接口,這里就不展開講。
看下面的隸屬 ApplicationContext 粉紅色的 “高級容器”,依賴著 “低級容器”,這里說的是依賴,不是繼承哦。他依賴著 “低級容器” 的 getBean 功能。而高級容器有更多的功能:支持不同的信息源頭,可以訪問文件資源,支持應(yīng)用事件(Observer 模式)。
通常用戶看到的就是 “高級容器”。 但 BeanFactory 也非常夠用啦!
左邊灰色區(qū)域的是 “低級容器”, 只負責加載 Bean,獲取 Bean。容器其他的高級功能是沒有的。例如上圖畫的 refresh 刷新 Bean 工廠所有配置。生命周期事件回調(diào)等。
好,解釋了低級容器和高級容器,我們可以看看一個 IoC 啟動過程是什么樣子的。說白了,就是 ClassPathXmlApplicationContext 這個類,在啟動時,都做了啥。(由于我這是 interface21 的代碼,肯定和你的 Spring 4.x 系列不同)。
下圖是 ClassPathXmlApplicationContext 的構(gòu)造過程,實際就是 Spring IoC 的初始化過程。
注意,這里為了理解方便,有所簡化。
這里再用文字來描述這個過程:
用戶構(gòu)造 ClassPathXmlApplicationContext(簡稱 CPAC)
CPAC 首先訪問了 “抽象高級容器” 的 final 的 refresh 方法,這個方法是模板方法。所以要回調(diào)子類(低級容器)的 refreshBeanFactory 方法,這個方法的作用是使用低級容器加載所有 BeanDefinition 和 Properties 到容器中。
簡單說就是:
低級容器 加載配置文件(從 XML,數(shù)據(jù)庫,Applet),并解析成 BeanDefinition 到低級容器中。
所以,一定要把 “低級容器” 和“高級容器” 的區(qū)別弄清楚。不能一葉障目不見泰山。
好,當我們創(chuàng)建好容器,就會使用 getBean 方法,獲取 Bean,而 getBean 的流程如下:
從圖中可以看出,getBean 的操作都是在低級容器里操作的。其中有個遞歸操作,這個是什么意思呢?
假設(shè):當 Bean_A 依賴著 Bean_B,而這個 Bean_A 在加載的時候,其配置的 ref = “Bean_B” 在解析的時候只是一個占位符,被放入了 Bean_A 的屬性集合中,當調(diào)用 getBean 時,需要真正 Bean_B 注入到 Bean_A 內(nèi)部時,就需要從容器中獲取這個 Bean_B,因此產(chǎn)生了遞歸。
為什么不是在加載的時候,就直接注入呢?因為加載的順序不同,很可能 Bean_A 依賴的 Bean_B 還沒有加載好,也就無法從容器中獲取,你不能要求用戶把 Bean 的加載順序排列好,這是不人道的。
所以,Spring 將其分為了 2 個步驟:
加載所有的 Bean 配置成 BeanDefinition 到容器中,如果 Bean 有依賴關(guān)系,則使用占位符暫時代替。
所以 ApplicationContext refresh 方法里面的操作不只是 IoC,是高級容器的所有功能(包括 IoC),IoC 的功能在低級容器里就可以實現(xiàn)。
說了這么多,不知道你有沒有理解Spring IoC? 這里小結(jié)一下:IoC 在 Spring 里,只需要低級容器就可以實現(xiàn),2 個步驟:
加載配置文件,解析成 BeanDefinition 放在 Map 里。
上面就是 Spring 低級容器(BeanFactory)的 IoC。
至于高級容器 ApplicationContext,他包含了低級容器的功能,當他執(zhí)行 refresh 模板方法的時候,將刷新整個容器的 Bean。同時其作為高級容器,包含了太多的功能。一句話,他不僅僅是 IoC。他支持不同信息源頭,支持 BeanFactory 工具類,支持層級容器,支持訪問文件資源,支持事件發(fā)布通知,支持接口回調(diào)等等。
可以預見,隨著 Spring 的不斷發(fā)展,高級容器的功能會越來越多。
誠然,了解 IoC 的過程,實際上為了了解 Spring 初始化時,各個接口的回調(diào)時機。例如 InitializingBean,BeanFactoryAware,ApplicationListener 等等接口,這些接口的作用,筆者之前寫過一篇文章進行介紹,有興趣可以看一下,關(guān)鍵字:Spring 必知必會 擴展接口。
但是請注意,實現(xiàn) Spring 接口代表著你這個應(yīng)用就綁定死 Spring 了!代表 Spring 具有侵入性!要知道,Spring 發(fā)布時,無侵入性就是他最大的宣傳點之一 —— 即 IoC 容器可以隨便更換,代碼無需變動。而現(xiàn)如今,Spring 已然成為 J2EE 社區(qū)準官方解決方案,也沒有了所謂的侵入性這個說法。因為他就是標準,和 Servlet 一樣,你能不實現(xiàn) Servlet 的接口嗎?: -)
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。