天天看點

《Spring技術内幕》——2.1節Spring IoC容器概述

spring framework的核心:ioc容器的實作

朝辭白帝彩雲間,千裡江陵一日還。

兩岸猿聲啼不住,輕舟已過萬重山。

—【唐】李白《早發白帝城》

本章内容

spring ioc容器概述

ioc容器系列的設計與實作:beanfactory和applicationcontext

ioc容器的初始化過程

ioc容器的依賴注入

容器其他相關特性的設計與實作

2.1.1 ioc容器和依賴反轉模式

子曰:溫故而知新。在這裡,我們先簡要地回顧一下依賴反轉的相關概念。我們選取維基百科中關于依賴反轉的叙述,把這些文字作為我們了解依賴反轉這個概念的參考。這裡不會對這些原理進行學理上的考究,隻是希望提供一些有用的資訊,以便給讀者一些啟示。這個模式非常重要,它是ioc容器得到廣泛應用的基礎。

維基百科對“依賴反轉”相關概念的叙述

早在2004年,martin fowler就提出了“哪些方面的控制被反轉了?”這個問題。他得出的結論是:依賴對象的獲得被反轉了。基于這個結論,他為控制反轉創造了一個更好的名字:依賴注入。許多非凡的應用(比helloworld.java更加優美、更加複雜)都是由兩個或多個類通過彼此的合作來實作業務邏輯的,這使得每個對象都需要與其合作的對象(也就是它所依賴的對象)的引用。如果這個擷取過程要靠自身實作,那麼如你所見,這将導緻代碼高度耦合并且難以測試。

以上的這段話概括了依賴反轉的要義,如果合作對象的引用或依賴關系的管理由具體對象來完成,會導緻代碼的高度耦合和可測試性的降低,這對複雜的面向對象系統的設計是非常不利的。在面向對象系統中,對象封裝了資料和對資料的處理,對象的依賴關系常常展現在對資料和方法的依賴上。這些依賴關系可以通過把對象的依賴注入交給架構或ioc容器來完成,這種從具體對象手中交出控制的做法是非常有價值的,它可以在解耦代碼的同時提高代碼的可測試性。在極限程式設計中對單元測試和重構等實踐的強調展現了在軟體開發過程中對品質的承諾,這是軟體項目成功的一個重要因素。

依賴控制反轉的實作有很多種方式。在spring中,ioc容器是實作這個模式的載體,它可以在對象生成或初始化時直接将資料注入到對象中,也可以通過将對象引用注入到對象資料域中的方式來注入對方法調用的依賴。這種依賴注入是可以遞歸的,對象被逐層注入。就此而言,這種方案有一種完整而簡潔的美感,它把對象的依賴關系有序地建立起來,簡化了對象依賴關系的管理,在很大程度上簡化了面向對象系統的複雜性。

關于如何反轉對依賴的控制,把控制權從具體業務對象手中轉交到平台或者架構中,是降低面向對象系統設計複雜性和提高面向對象系統可測試性的一個有效的解決方案。它促進了ioc設計模式的發展,是ioc容器要解決的核心問題。同時,也是産品化的ioc容器出現的推動力。

注意 ioc亦稱為“依賴倒置原理”(dependency inversion principle),幾乎所有架構都使用了倒置注入(martin fowler)技巧,是ioc原理的一項應用。smalltalk、c++、java和.net等面向對象語言的程式員已使用了這些原理。控制反轉是spring架構的核心。

ioc原理的應用在不同的語言中有很多實作,比如smalltalk、c++、java等。在同一語言的實作中也會有多個具體的産品,spring是java語言實作中最著名的一個。同時,ioc也是spring架構要解決的核心問題。

注意 應用控制反轉後,當對象被建立時,由一個調控系統内的所有對象的外界實體将其所依賴的對象的引用傳遞給它,即依賴被注入到對象中。是以,控制反轉是關于一個對象如何擷取它所依賴的對象的引用,在這裡,反轉指的是責任的反轉。

我們可以認為上面提到的調控系統是應用平台,或者更具體地說是ioc容器。通過使用ioc容器,對象依賴關系的管理被反轉了,轉到ioc容器中來了,對象之間的互相依賴關系由ioc容器進行管理,并由ioc容器完成對象的注入。這樣就在很大程度上簡化了應用的開發,把應用從複雜的對象依賴關系管理中解放出來。簡單地說,因為很多對象依賴關系的建立和維護并不需要和系統運作狀态有很強的關聯性,是以可以把在面向對象程式設計中需要執行的諸如建立對象、為對象引用指派等操作交由容器統一完成。這樣一來,這些散落在不同代碼中的功能相同的部分就集中成為容器的一部分,也就是成為面向對象系統的基礎設施的一部分。

如果對面向對象系統中的對象進行簡單分類,會發現除了一部分是資料對象外,其他很大一部分對象是用來處理資料的。這些對象并不常發生變化,是系統中基礎的部分。在很多情況下,這些對象在系統中以單件的形式起作用就可以滿足應用的需求,而且它們也不常涉及資料和狀态共享的問題。如果涉及資料共享方面的問題,需要在這些單件的基礎上再做進一步的處理。

同時,這些對象之間的互相依賴關系也是比較穩定的,一般不會随着應用的運作狀态的改變而改變。這些特性使這些對象非常适合由ioc容器來管理,雖然它們存在于應用系統中,但是應用系統并不承擔管理這些對象的責任,而是通過依賴反轉把責任交給了容器(或者說平台)。了解了這些背景,spring ioc容器的原理也就不難了解了。在原理的具體實作上,spring有着自己的獨特思路、實作技巧和豐富的産品特性。關于這些原理的實作,下面會進行詳細的分析。

2.1.2 spring ioc的應用場景

在java ee企業應用開發中,前面介紹的ioc(控制反轉)設計模式,是解耦元件之間複雜關系的利器, spring ioc子產品就是這個模式的一種實作。在以spring為代表的輕量級java ee開發風行之前,我們更熟悉的是以ejb為代表的開發模式。在ejb模式中,應用開發人員需要編寫ejb元件,而這種元件需要滿足ejb容器的規範,才能夠運作在ejb容器中,進而擷取事務管理、生命周期管理這些元件開發的基本服務。從擷取的基本服務上看,spring提供的服務和ejb容器提供的服務并沒有太大的差别,隻是在具體怎樣擷取服務的方式上,兩者的設計有很大的不同:在spring中,spring ioc提供了一個基本的javabean容器,通過ioc模式管理依賴關系,并通過依賴注入和aop切面增強了為javabean這樣的pojo對象賦予事務管理、生命周期管理等基本功能;而對于ejb,一個簡單的ejb元件需要編寫遠端/本地接口、home接口以及bean的實作類,而且ejb運作是不能脫離ejb容器的,查找其他ejb元件也需要通過諸如jndi這樣的方式,進而造成了對ejb容器和技術規範的依賴。也就是說spring把ejb元件還原成了pojo對象或者javabean對象,降低了應用開發對傳統j2ee技術規範的依賴。

同時,在應用開發中,以應用開發人員的身份設計元件時,往往需要引用和調用其他元件的服務,這種依賴關系如果固化在元件設計中,就會造成依賴關系的僵化和維護難度的增加,這個時候,如果使用ioc容器,把資源擷取的方向反轉,讓ioc容器主動管理這些依賴關系,将這些依賴關系注入到元件中,那麼會讓這些依賴關系的适配和管理更加靈活。在具體的注入實作中,接口注入(type 1 ioc)、setter注入(type 2 ioc)、構造器注入(type 3 ioc)是主要的注入方式。在spring的ioc設計中,setter注入和構造器注入是主要的注入方式;相對而言,使用spring時setter注入是常見的注入方式,而且為了防止注入異常,spring ioc容器還提供了對特定依賴的檢查。

另一方面,在應用管理依賴關系時,可以通過ioc容器将控制進行反轉,在反轉的實作中,如果能通過可讀的文本來完成配置,并且還能通過工具對這些配置資訊進行可視化的管理和浏覽,那麼肯定是能夠提高對元件關系的管理水準,并且如果耦合關系需要變動,并不需要重新修改和編譯java源代碼,這符合在面向對象設計中的開閉準則,并且能夠提高元件系統設計的靈活性,同時,如果結合osgi的使用特性,還可以提高應用的動态部署能力。

在具體使用spring ioc容器的時候,我們可以看到,spring ioc容器已經是一個産品實作。作為産品實作,它對多種應用場景的适配是通過spring設計的ioc容器系列來實作的,比如在某個容器系列中可以看到各種帶有不同容器特性的實作,可以讀取不同配置資訊的各種容器,從不同i/o源讀取配置資訊的各種容器設計,更加面向架構的容器應用上下文的容器設計等。這些豐富的容器設計,已經可以滿足廣大使用者對ioc容器的各種使用需求,這時的spring ioc容器,已經不是原來簡單的interface21架構了,已經成為一個ioc容器的工業級實作。下面,我們會對ioc容器系列的設計和實作進行詳細分析。