1. 程式人生 > >Spring的控制反轉的通俗解釋

Spring的控制反轉的通俗解釋

套用好萊塢的一句名言就是:你呆著別動,到時我會找你。 
什麼意思呢?就好比一個皇帝和太監 
有一天皇帝想幸某個美女,於是跟太監說,今夜我要寵幸美女 
皇帝往往不會告訴太監,今晚幾點會回宮,會回哪張龍床,他只會告訴太監他要哪位美女 
其它一切都交由太監去安排,到了晚上皇帝回宮時,自然會有美女出現在皇帝的龍床上 
這就是控制反轉,而把美女送到皇帝的寢宮裡面去就是注射 
太監就是是框架裡面的注射控制器類BeanFactory,負責找到美女並送到龍床上去 
整個後宮可以看成是Spring框架,美女就是Spring控制下的JavaBean 
而傳統的模式就是一個飢渴男去找小姐出臺 
找領班,幫助給介紹一個云云,於是領班就開始給他張羅 
介紹一個合適的給他,完事後,再把小姐還給領班,下次再來 
這個過程中,領班就是查詢上下文Context,領班的一個職能就是給客戶找到他們所要的小姐 
這就是lookup()方法,領班手中的小姐名錄就是JNDI//Java Naming and Directory Interface 
小姐就是EJB,飢渴男是客戶端,青樓是EJB容器 
看到區別了麼?飢渴男去找小姐出臺很麻煩,不僅得找,用完後還得把小姐給還回去 
而皇帝爽翻了,什麼都不用管,交給太監去處理,控制權轉移到太監手中去了 
而不是皇帝,必要時候由太監給注射進去就可以了 
看到Spring的美妙了吧,Spring還提供了與多個主流框架的支援 
可以和其它開源框架整合

IoC,用白話來講,就是由容器控制程式之間的關係,而非傳統實現中,由程式程式碼直接操控。這也 
就是所謂“控制反轉”的概念所在:控制權由應用程式碼中轉到了外部容器,控制權的轉移,是所謂反轉。 
USB介面例子: 
膝上型電腦與外圍儲存裝置通過預先指定的一個介面(USB)相連,對於筆記本而言, 
只是將使用者指定的資料傳送到USB介面,而這些資料何去何從,則由當前接入的USB裝置決定。在USB 
裝置載入之前,筆記本不可能預料使用者將在USB介面上接入何種裝置,只有USB裝置接入之後,這種設 
備之間的依賴關係才開始形成。 
對應上面關於依賴注入機制的描述,在執行時(系統開機,USB 裝置載入)由容器(執行在筆記本 
中的Windows作業系統)將依賴關係(筆記本依賴USB裝置進行資料存取)注入到元件中(Windows 
檔案訪問元件)。 
理解: 
傳統模式中是類和類之間直接呼叫,所以有很強的耦合度,程式之間的依賴關係比較強,後期維護時牽扯的比較多。
IOC,用配置檔案(XML)來描述類與類之間的關係,由容器來管理,降低了程式間的耦合度,程式的修改可以通過簡單的配置檔案修改來實現

面向切面程式設計AOP

Aspect Oriented Programming(面向切面程式設計),可以 通過預編譯方式和執行期動態代理實現在不修改原始碼的情況下給程式動態統一 新增功能的一種技術。

6.2.1  IOC原理

IoC,直觀地講,就是容器控制程式之間的關係,而非傳統實現中,由程式程式碼直接操控。這也就是所謂“控制反轉”的概念所在。控制權由應用程式碼中轉到了外部容器,控制權的轉移是所謂反轉。IoC還有另外一個名字——“依賴注入(Dependency Injection)”。從名字上理解,所謂依賴注入,即元件之間的依賴關係由容器在執行期決定,形象地說,即由容器動態地將某種依賴關係注入到元件之中。

下面通過一個生動形象的例子介紹控制反轉。

比如,一個女孩希望找到合適的男朋友,如圖6-2所示,可以有3種方式,即青梅竹馬、親友介紹、父母包辦。

第1種方式是青梅竹馬,如圖6-3所示。

通過程式碼表示如下。

public class Girl {

  void kiss(){

    Boy boy = new Boy();

  }

}

第2種方式是親友介紹,如圖**所示。

通過程式碼表示如下。

public class Girl {

  void kiss(){

    Boy boy = BoyFactory.createBoy();

  }

}

第3種方式是父母包辦,如圖6-5所示。

通過程式碼表示如下。

public class Girl {

  void kiss(Boy boy){

    // kiss boy

   boy.kiss();

  }

}

哪一種為控制反轉IoC呢?雖然在現實生活中我們都希望青梅竹馬,但在Spring世界裡,選擇的卻是父母包辦,它就是控制反轉,而這裡具有控制力的父母,就是Spring所謂的容器概念。

典型的IoC可以如圖6-6所示。

IoC的3種依賴注入型別如下。

第1種是通過介面注射,這種方式要求我們的類必須實現容器給定的一個介面,然後容器會利用這個介面給我們這個類注射它所依賴的類。

public class Girl implements Servicable {

  Kissable kissable;

  public void service(ServiceManager mgr) {

    kissable = (Kissable) mgr.lookup("kissable");

  }

  public void kissYourKissable() {

    kissable.kiss();

  }

}

<container>

  <component name="kissable" class="Boy">      

    <configuration> … </configuration>

  </component>

  <component name="girl" class="Girl" />

</container>

第2種是通過setter方法注射,這種方式也是Spring推薦的方式

public class Girl {

  private Kissable kissable;

  public void setKissable(Kissable kissable) {

    this.kissable = kissable;

  }

  public void kissYourKissable() {

    kissable.kiss();

  }

}

<beans>

  <bean id="boy" class="Boy"/>

  <bean id="girl" class="Girl">

    <property name="kissable">

      <ref bean="boy"/>

    </property>

  </bean>

</beans>

第3種是通過構造方法注射類,這種方式Spring同樣給予了實現,它和通過setter方式一樣,都在類裡無任何侵入性,但是,不是沒有侵入性,只是把侵入性轉移了,顯然第1種方式要求實現特定的介面,侵入性非常強,不方便以後移植。

public class Girl {