控制反轉(IoC)與依賴注入(DI)

1. 控制反轉 (Inversion of Control) 與依賴注入 (Dependency Injection)
控制反轉即 IoC (Inversion of Control) ,它把傳統上由程序代碼直接操控的對象的調用權交給容器,經過容器來實現對象組件的裝配和管理。所謂的「控制反轉」概念就是對組件對象控制權的轉移,從程序代碼自己轉移到了外部容器。
IoC 是一個很大的概念,能夠用不一樣的方式來實現。其主要實現方式有兩種: <1> 依賴查找( Dependency Lookup ):容器提供回調接口和上下文環境給組件 EJB Apache Avalon 都使用這種方式。 <2> 依賴注入( Dependency Injection ):組件不作定位查詢,只提供普通的 Java 方法讓容器去決定依賴關係。後者是時下最流行的 IoC 類型,其又有接口注入( Interface Injection ),設值注入( Setter Injection )和構造子注入( Constructor Injection )三種方式。
1 控制反轉概念結構
依賴注入之因此更流行是由於它是一種更可取的方式:讓容器全權負責依賴查詢,受管組件只須要暴露 JavaBean setter 方法或者帶參數的構造子或者接口,使容器能夠在初始化時組裝對象的依賴關係。其與依賴查找方式相比,主要優點爲: <1> 查找定位操做與應用代碼徹底無關。 <2> 不依賴於容器的 API ,能夠很容易地在任何容器之外使用應用對象。 <3> 不須要特殊的接口,絕大多數對象能夠作到徹底沒必要依賴容器。
 
2. 好萊塢原則
IoC 體現了好萊塢原則,即「不要打電話過來,咱們會打給你」。第一次遇到好萊塢原則是在瞭解模板方法 (Template Mathod) 模式的時候,模板方法模式的核心是,基類(抽象類)定義了算法的骨架,而將一些步驟延遲到子類中。
2 模板方法模式類圖
 
如今來考慮 IoC 的實現機制,組件定義了整個流程框架,而其中的一些業務邏輯的實現要藉助於其餘業務對象的加入,它們能夠經過兩種方式參與到業務流程中,一種是依賴查找( Dependency Lookup ),相似與 JDNI 的實現,經過 JNDI 來找到相應的業務對象(代碼 1 ),另外一種是依賴注入,經過 IoC 容器將業務對象注入到組件中。
 
3. 依賴查找( Dependency Lookup
下面代碼展現了基於 JNDI 實現的依賴查找機制。
public class MyBusniessObject{
  private DataSource ds;
  private MyCollaborator myCollaborator;
 
  public MyBusnissObject(){
Context ctx = null;
try{
    ctx = new InitialContext();
    ds = (DataSource) ctx.lookup(「java:comp/env/dataSourceName」);
    myCollaborator =
 (MyCollaborator) ctx.lookup(「java:comp/env/myCollaboratorName」);
    }……
代碼 1 依賴查找( Dependency Lookup )代碼實現
依賴查找的主要問題是,這段代碼必須依賴於 JNDI 環境,因此它不能在應用服務器以外運行,而且若是要用別的方式取代 JNDI 來查找資源和協做對象,就必須把 JNDI 代碼抽出來重構到一個策略方法中去。
 
4. 依賴注入( Dependency Injection
依賴注入的基本原則是:應用組件不該該負責查找資源或者其餘依賴的協做對象。配置對象的工做應該由 IoC 容器負責,「查找資源」的邏輯應該從應用組件的代碼中抽取出來,交給 IoC 容器負責。
下面分別演示 3 中注入機制。
代碼 2 待注入的業務對象 Content.java
package com.zj.ioc.di;
 
public class Content {
 
    public void BusniessContent(){
       System. out .println( "do business" );
    }
   
    public void AnotherBusniessContent(){
       System. out .println( "do another business" );
    }
}
MyBusniess 類展現了一個業務組件,它的實現須要對象 Content 的注入。代碼 3 ,代碼 4 ,代碼 5 6 分別演示構造子注入( Constructor Injection ),設值注入( Setter Injection )和接口注入( Interface Injection )三種方式。
 
代碼 3 構造子注入( Constructor Injection MyBusiness.java
package com.zj.ioc.di.ctor;
import com.zj.ioc.di.Content;
 
public class MyBusiness {
    private Content myContent ;
 
    public MyBusiness(Content content) {
       myContent = content;
    }
   
    public void doBusiness(){
       myContent .BusniessContent();
    }
   
    public void doAnotherBusiness(){
       myContent .AnotherBusniessContent();
    }
}
 
代碼 4 設值注入( Setter Injection MyBusiness.java
package com.zj.ioc.di.set;
import com.zj.ioc.di.Content;
 
public class MyBusiness {
    private Content myContent ;
 
    public void setContent(Content content) {
       myContent = content;
    }
   
    public void doBusiness(){
       myContent .BusniessContent();
    }
   
    public void doAnotherBusiness(){
       myContent .AnotherBusniessContent();
    }
}
 
代碼 5 設置注入接口 InContent.java
package com.zj.ioc.di.iface;
import com.zj.ioc.di.Content;
 
public interface InContent {
    void createContent(Content content);
}
 
代碼 6 接口注入( Interface Injection MyBusiness.java
package com.zj.ioc.di.iface;
import com.zj.ioc.di.Content;
 
public class MyBusiness implements InContent{
    private Content myContent ;
 
    public void createContent(Content content) {
       myContent = content;
    }
   
    public void doBusniess(){
       myContent .BusniessContent();
    }
   
    public void doAnotherBusniess(){
       myContent .AnotherBusniessContent();
    }
}
 
5. 依賴拖拽 (Dependency Pull)
最後須要介紹的是依賴拖拽,注入的對象如何與組件發生聯繫,這個過程就是經過依賴拖拽實現
代碼 7 依賴拖拽示例
public static void main(String[] args) throws Exception{
//get the bean factory
BeanFactory factory = getBeanFactory();
MessageRender mr = (MessageRender) factory.getBean(「renderer」);
mr.render();
}
而一般 對注入對象 的配置能夠經過一個 xml 文件完成。
使用這種方式對對象進行集中管理,使用依賴拖拽與依賴查找本質的區別是,依賴查找是在業務組件代碼中進行的,而不是從一個集中的註冊處,特定的地點執行。
相關文章
相關標籤/搜索