阿里面試常問Spring IOC解析,不得不會的知識點。

廣義的 IOC

1.IoC(Inversion of Control) 控制反轉,即「不用打電話過來,咱們會打給你」。 兩種實現: 依賴查找(DL)和依賴注入(DI)。面試

IOC 和 DI 、DL 的關係(這個 DL,Avalon 和 EJB 就是使用的這種方式實現的 IoC): 數據庫

2.DL 已經被拋棄,由於他須要用戶本身去是使用 API 進行查找資源和組裝對象。即有侵入性。

3.DI 是 Spring 使用的方式,容器負責組件的裝配。工具

注意:Java 使用 DI 方式實現 IoC 的不止 Spring,包括 Google 的 Guice,還有一個冷門的 PicoContainer(極度輕量,但只提供 IoC)。ui

Spring 的 IoC

Spring 的 IoC 設計支持如下功能:設計

  1. 依賴注入
  2. 依賴檢查
  3. 自動裝配
  4. 支持集合
  5. 指定初始化方法和銷燬方法

支持回調某些方法(可是須要實現 Spring 接口,略有侵入) 其中,最重要的就是依賴注入,從 XML 的配置上說, 即 ref 標籤。對應 Spring RuntimeBeanReference 對象。cdn

對於 IoC 來講,最重要的就是容器。容器管理着 Bean 的生命週期,控制着 Bean 的依賴注入。 那麼, Spring 如何設計容器的呢?server

Spring 做者 Rod Johnson 設計了兩個接口用以表示容器。對象

  1. BeanFactory
  2. ApplicationContext BeanFactory 粗暴簡單,能夠理解爲就是個 HashMap,Key 是 BeanName,Value 是 Bean 實例。一般只提供註冊(put),獲取(get)這兩個功能。咱們能夠稱之爲 「低級容器」

ApplicationContext 能夠稱之爲 「高級容器」。由於他比 BeanFactory 多了更多的功能。他繼承了多個接口。所以具有了更多的功能。blog

例如資源的獲取,支持多種消息(例如 JSP tag 的支持),對 BeanFactory 多了工具級別的支持等待。因此你看他的名字,已經不是 BeanFactory 之類的工廠了,而是 「應用上下文」, 表明着整個大容器的全部功能。繼承

該接口定義了一個 refresh 方法,此方法是全部閱讀 Spring 源碼的人的最熟悉的方法,用於刷新整個容器,即從新加載/刷新全部的 bean。

固然,除了這兩個大接口,還有其餘的輔助接口,但我今天不會花太多篇幅介紹他們。

爲了更直觀的展現 「低級容器」 和 「高級容器」 的關係,我這裏經過經常使用的 ClassPathXmlApplicationContext 類,來展現整個容器的層級 UML 關係。

有點複雜? 先不要慌,我來解釋一下。

最上面的 BeanFactory 知道吧?我就不講了。

下面的 3 個綠色的,都是功能擴展接口,這裏就不展開講。

看下面的隸屬 ApplicationContext 粉紅色的 「高級容器」,依賴着 「低級容器」,這裏說的是依賴,不是繼承哦。他依賴着 「低級容器」 的 getBean 功能。而高級容器有更多的功能:支持不一樣的信息源頭,能夠訪問文件資源,支持應用事件(Observer 模式)。

一般用戶看到的就是 「高級容器」。 但 BeanFactory 也很是夠用啦!

左邊灰色區域的是 「低級容器」, 只負載加載 Bean,獲取 Bean。容器其餘的高級功能是沒有的。例如上圖畫的 refresh 刷新 Bean 工廠全部配置。生命週期事件回調等。

好,解釋了低級容器和高級容器,咱們能夠看看一個 IoC 啓動過程是什麼樣子的。說白了,就是 ClassPathXmlApplicationContext 這個類,在啓動時,都作了啥。(因爲我這是 interface21 的代碼,確定和你的 Spring 4.x 系列不一樣)。

下圖是 ClassPathXmlApplicationContext 的構造過程,實際就是 Spring IoC 的初始化過程。

注意,這裏爲了理解方便,有所簡化。

這裏再用文字來描述這個過程:

  1. 用戶構造 ClassPathXmlApplicationContext(簡稱 CPAC)

  2. CPAC 首先訪問了 「抽象高級容器」 的 final 的 refresh 方法,這個方法是模板方法。因此要回調子類(低級容器)的 refreshBeanFactory 方法,這個方法的做用是使用低級容器加載全部 BeanDefinition 和 Properties 到容器中。

  3. 低級容器加載成功後,高級容器開始處理一些回調,例如 Bean 後置處理器。回調 setBeanFactory 方法。或者註冊監聽器等,發佈事件,實例化單例 Bean 等等功能,這些功能,隨着 Spring 的不斷升級,功能愈來愈多,不少人在這裏迷失了方向 :)。

簡單說就是:

  1. 低級容器 加載配置文件(從 XML,數據庫,Applet),並解析成 BeanDefinition 到低級容器中。
  2. 加載成功後,高級容器啓動高級功能,例如接口回調,監聽器,自動實例化單例,發佈事件等等功能。

因此,必定要把 「低級容器」 和「高級容器」 的區別弄清楚。不能一葉障目不見泰山。

好,當咱們建立好容器,就會使用 getBean 方法,獲取 Bean,而 getBean 的流程以下:

從圖中能夠看出,getBean 的操做都是在低級容器裏操做的。其中有個遞歸操做,這個是什麼意思呢?

假設:當 Bean_A 依賴着 Bean_B,而這個 Bean_A 在加載的時候,其配置的 ref = 「Bean_B」 在解析的時候只是一個佔位符,被放入了 Bean_A 的屬性集合中,當調用 getBean 時,須要真正 Bean_B 注入到 Bean_A 內部時,就須要從容器中獲取這個 Bean_B,所以產生了遞歸。

爲何不是在加載的時候,就直接注入呢?由於加載的順序不一樣,極可能 Bean_A 依賴的 Bean_B 尚未加載好,也就沒法從容器中獲取,你不能要求用戶把 Bean 的加載順序排列好,這是不人道的。

因此,Spring 將其分爲了 2 個步驟:

  1. 加載全部的 Bean 配置成 BeanDefinition 到容器中,若是 Bean 有依賴關係,則使用佔位符暫時代替。
  2. 而後,在調用 getBean 的時候,進行真正的依賴注入,即若是碰到了屬性是 ref 的(佔位符),那麼就從容器裏獲取這個 Bean,而後注入到實例中 —— 稱之爲依賴注入。

能夠看到,依賴注入實際上,只須要 「低級容器」 就能夠實現。

這就是 IoC。

因此 ApplicationContext refresh 方法裏面的操做不僅是 IoC,是高級容器的全部功能(包括 IoC),IoC 的功能在低級容器裏就能夠實現。

總結

說了這麼多,不知道你有沒有理解Spring IoC? 這裏小結一下:IoC 在 Spring 裏,只須要低級容器就能夠實現,2 個步驟:

  1. 加載配置文件,解析成 BeanDefinition 放在 Map 裏。

  2. 調用 getBean 的時候,從 BeanDefinition 所屬的 Map 裏,拿出 Class 對象進行實例化,同時,若是有依賴關係,將遞歸調用 getBean 方法 —— 完成依賴注入。

上面就是 Spring 低級容器(BeanFactory)的 IoC。

至於高級容器 ApplicationContext,他包含了低級容器的功能,當他執行 refresh 模板方法的時候,將刷新整個容器的 Bean。同時其做爲高級容器,包含了太多的功能。一句話,他不只僅是 IoC。他支持不一樣信息源頭,支持 BeanFactory 工具類,支持層級容器,支持訪問文件資源,支持事件發佈通知,支持接口回調等等。

能夠預見,隨着 Spring 的不斷髮展,高級容器的功能會愈來愈多。

誠然,瞭解 IoC 的過程,實際上爲了瞭解 Spring 初始化時,各個接口的回調時機。例如 InitializingBean,BeanFactoryAware,ApplicationListener 等等接口,這些接口的做用,筆者以前寫過一篇文章進行介紹,有興趣能夠看一下,關鍵字:Spring 必知必會 擴展接口。

可是請注意,實現 Spring 接口表明着你這個應用就綁定死 Spring 了!表明 Spring 具備侵入性!要知道,Spring 發佈時,無侵入性就是他最大的宣傳點之一 —— 即 IoC 容器能夠隨便更換,代碼無需變更。而現現在,Spring 已然成爲 J2EE 社區準官方解決方案,也沒有了所謂的侵入性這個說法。由於他就是標準,和 Servlet 同樣,你能不實現 Servlet 的接口嗎?: -)

好了,下次若是再有面試官問 Spring IoC 初始化過程,就不再會含糊其詞、支支吾吾了!!!

相關文章
相關標籤/搜索