淺析Spring之IoC

米奇羅發表於2019-03-23

前言

本篇部落格會簡單介紹Spring IoC容器的知識,認識控制反轉及依賴注入。

1. IoC概述

IoC ( Inverse of Control,控制反轉 )是Spring容器的核心,AOP、宣告式實務等功能都在此基礎上開花結果,但是IoC的概念卻比較晦澀難懂,它涉及程式碼解耦、設計模式、程式碼優化等問題的考量,下面通過簡單的例子說明這個概念。

1.1 通過例項理解IoC概念

用一個簡單的劇本--《墨攻》來理解。 《墨攻》這部電影涉及三個解釋,角色墨者革離、飾演者劉德華及劇本--革離的救國行為(暫且稱為墨攻)。

public class MoAttack{
  public void cityGateAsk(){
    LiuDeHua ldh = new LiuDeHua;
	ldh.responseAsk("我是墨者革離!");
  }
}
複製程式碼

從上面劇本我們可以發現,具體飾演者劉德華直接侵入劇本,使劇本和演員直接耦合在一起。 而一部電影的編劇創作時應該圍繞故事的角色進行,而不是圍繞角色的飾演者進行。這樣在拍攝時才能保證可以自由地遴選合適的演員。 此時MoAttack同時依賴GiLi介面和LiuDeHua介面,並沒有達到我們的期望劇本僅依賴於角色。那怎麼讓LiuDeHua和劇本無關而又能完成GeLi的具體動作呢? 這就需要引入導演,導演將LiuDeHua安排在GeLi這個角色上,導負責劇本、角色、演員的協調控制。如圖所示:

淺析Spring之IoC

通過引入導演,使劇本和演員解耦,對應軟體中,導演就像一臺裝配器,安排演員的表演的具體角色。

反過來理解IoC容器,IoC的字面意思是控制反轉,它包括兩個方面:

  • 其一是控制
  • 其二是反轉

對應前面例子,"控制"是指選擇GeLi角色的控制權,"反轉"是指這種控制權從劇本中移除,轉交到導演手中。對於軟體來說,即某一介面具實現類的選擇控制權從呼叫類中移除,轉交給第三方決定,即由Spring容器藉由Bean配置來控制。

因為IoC確實不夠開門見山,最終軟體界泰斗級人物Martin Fowler 提出了DI(Dependency Injection,依賴注入)的概念來代替IoC 。依賴注入顯然比控制反轉直接明瞭、易於理解。 我們來看一下這篇權威的文章:Inversion of Control Containers and the Dependency Injectionpattern

2. 控制反轉與依賴注入的關係

在一篇部落格中看到這種說法:IoC是一種思想,DI是一種設計模式,實現IoC的模式。 我個人認為IoC 不只是一種思想。更多是行為,將主控權交予第三方的行為,實現了程式的主控權的反轉。就像上面所講述的,將角色、劇本、演員的主控權交予導演(第三方)。Martin Fowler認為“控制反轉”這個概念太泛了,於是決定將這個模式叫做”依賴注入”(Dependency Injection)。

3. 依賴注入的三種方法

從注入方法上看,IoC主要可以劃分為3中種型別:建構函式注入、屬性注入和介面注入。Spring支援建構函式注入和屬性注入。

3.1 建構函式注入

在建構函式中,通過呼叫類的建構函式,將介面實現類通過構造函變數傳入。

public class MoAttack{
  private GeLi geli;

  //注入革離得具體飾演者
  public MoAttack(GeLi geli){
    this.geli = geli;
  }
  public void cityGateAsk(){
    geli..responseAsk("墨者革離!");
  }
}
複製程式碼

MoAttack 的建構函式不關心具體由誰飾演革離這個角色,只需按劇本完成相應的表演即可,角色的具體飾演者將由導演來安排。

public class Directer{
  public void directer(){

    //指定角色的飾演者
    GeLi geli = new LiuDeHua();

    //注入具體飾演者到劇本中
    MoAttack moAttack = new MoAttack(geli);
    moAttack.cityGateAsk();
  }
}
複製程式碼

3.2 屬性注入

對於JavaBean物件來說,通常會通過setXXX()和getXXX()來訪問對應屬性。通過setter方法,可以更改相應的物件屬性。所以當前物件只要為其依賴物件所對應的屬性新增setter方法,就可以通過setter方法將相應的依賴物件設定到被注入物件中。

public class MoAttack{
  private GeLi geli;

  //屬性注入的方法
  public MoAttack(GeLi geli){
    this.geli = geli;
  }
  public void cityGateAsk(){
    geli..responseAsk("墨者革離!");
  }
}
複製程式碼
public class Directer{
  public void directer(){

    MoAttack moAttack = new MoAttack();

    //呼叫屬性setter方法注入
    GeLi geli = new LiuDeHua();
    moAttack.setGeli(geli);
    moAttack.cityGateAsk();
  }
}
複製程式碼

3.3 介面注入

除了前面兩種注入技術,還可以在介面中定義需要注入的資訊,並通過介面完成注入。首先,我需要定義一個介面,元件的注入將通過這個介面進行。 但是從注入方式的使用來說,介面注入是現在不提倡的一種方式,基本處於”退役”狀態,因為它強制被注入物件實現不必要的介面。

4. 通過容器完成依賴關係的注入

除了上述幾種方法還有一種方法時通過容器來完成注入。這種方法因為涉及Java的反射知識和類裝載器,所以就不詳細介紹了。 簡單理解容器完成依賴注入,就是一個第三方容器來幫助類的初始化與裝配工作,讓開發者從這些底層實現類的例項化、依賴關係裝配等工作中解脫出來,專注於更有意義的業務邏輯開發工作。

5. 小結

對於初學Spring來說,IoC 是可以幫助我們解耦各業務物件間依賴關係的物件繫結方式,理解Ioc和DI能夠更好地幫助我們使用Spring框架。

6. 參考資料

相關文章