1.1 Spring IOC
IOC,全稱(Inverse Of Control),中文意思爲:控制反轉,Spring框架的核心基於控制反轉原理。
什麼是控制反轉?
控制反轉是一種將組件依賴關係的創建和管理置於程序外部的技術。
l 由容器控制程序之間的關係,而不是由代碼直接控制
l 由於控制權由代碼轉向了容器,所以稱爲反轉
對象與對象之間的關係可以簡單的理解爲對象之間的依賴關係:
依賴關係:在A類需要類B的一個實例來進行某些操作,比如在類A的方法中需要調用類B的方法來完成功能,叫做A類依賴於B類。
一個需要特定的依賴的組件一般會涉及一個依賴對象,在IOC的概念中叫做目標(target)。換句話說,IOC提供了這樣的服務,使一個組件能夠在它的整個生命週期中訪問它的依賴和服務,用這種方法與它的依賴進行交互。總的來說,IOC能夠被分解爲兩種子類型:依賴注入和依賴查找。
1.1.1 依賴查找
比如使用JNDI註冊一個數據庫連接池的示例中,代碼中從註冊處獲得依賴關係的JNDI查找(JNDI lookups):
initContext = new InitialContext(); //獲取數據源 DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/mysql"); |
1.1.2 依賴注入
(Dependency Injection)依賴注入:兩個對象之間的依賴關係在程序運行時由外部容器動態的注入依賴行爲方式稱爲依賴注入(DI)。DI是IOC的一種形式。
IoC在應用開發中是一個非常有力的概念。如Martin Flower所述,IoC的一種表現形式就是依賴性注射。依賴性注射用的是好萊塢原則,"不要找我,我會找你的。"。換句來說,你的類不會去查找或是實例化它們所依賴的類。控制恰好是反過來的,某種容器會設置這種依賴關係。使用IoC常常使代碼更加簡潔,並且爲相互依賴的類提供一種很好的方法。
依賴注入的三種實現類型:接口注入、Setter注入和構造器注入。
1.1.2.1 接口注入(Type1)
public class ClassA { private InterfaceB clzB; public void doSomething() { Ojbect obj =Class.forName(Config.BImplementation).newInstance(); clzB = (InterfaceB)obj; clzB.doIt() } …… } |
上面的代碼中,ClassA依賴於InterfaceB的實現,如何獲得InterfaceB實現類的實例?傳統的方法是在代碼中創建InterfaceB實現類的實例,並將起賦予clzB。
而這樣一來,ClassA在編譯期即依賴於InterfaceB的實現。爲了將調用者與實現者在編譯期分離,於是有了上面的代碼,我們根據預先在配置文件中設定的實現類的類名(Config.BImplementation),動態加載實現類,並通過InterfaceB強制轉型後爲ClassA所用。這就是接口注入的一個最原始的雛形。
而對於一個Type1型IOC容器而言,加載接口實現並創建其實例的工作由容器完成,見如下代碼。
public class ClassA { private InterfaceB clzB; public Object doSomething(InterfaceB b) { clzB = b; return clzB.doIt(); } …… } |
在運行期,InterfaceB實例將由容器提供。
Type1型IOC發展較早(有意或無意),在實際中得到了普遍應用,即使在IOC的概念尚未確立時,這樣的方法也已經頻繁出現在我們的代碼中。
下面的代碼大家應該非常熟悉:
public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException { …… } } |
這也是一個Type1 型注入,HttpServletRequest和HttpServletResponse實例由Servlet Container在運行期動態注入。
1.1.2.2 Setter(設值)注入(Type2)
各種類型的依賴注入模式中,設值注入模式在實際開發中得到了最廣泛的應用。
public class ClassA { private InterfaceB clzB; public void setClzB(InterfaceB clzB) { this. clzB = clzB; } …… } |
詳見Hello World程序
1.1.2.3 構造器注入(Type3)
依賴關係是通過類構造函數建立的
容器通過調用類的構造方法將其所需的依賴關係注入其中
public class DIByConstructor { private final DataSource dataSource; public DIByConstructor(DataSource ds) { this.dataSource = ds; } …… } |
1.1.2.4 三種依賴注入方式的比較
接口注入:
接口注入模式因爲歷史較爲悠久,在很多容器中都已經得到應用。但由於其在靈活性、易用性上不如其他兩種注入模式,因而在IOC的專題世界內並不被看好。
Setter注入:
l 對於習慣了傳統javabean開發的程序員,通過setter方法設定依賴關係更加直觀。
l 如果依賴關係較爲複雜,那麼構造子注入模式的構造函數也會相當龐大,而此時設值注入模式則更爲簡潔。
l 如果用到了第三方類庫,可能要求我們的組件提供一個默認的構造函數,此時構造子注入模式也不適用。
構造器注入:
l 在構造期間完成一個完整的、合法的對象。
l 所有依賴關係在構造函數中集中呈現。
l 依賴關係在構造時由容器一次性設定,組件被創建之後一直處於相對“不變”的穩定狀態。
l 只有組件的創建者關心其內部依賴關係,對調用者而言,該依賴關係處於“黑盒”之中。
總結:
可見,Type3和Type2模式各有千秋,而Spring、PicoContainer都對Type3和Type2類型的依賴注入機制提供了良好支持。這也就爲我們提供了更多的選擇餘地。理論上,以Type3類型爲主,輔之以Type2類型機制作爲補充,可以達到最好的依賴注入效果,不過對於基於Spring Framework開發的應用而言,Type2使用更加廣泛。