Day 2 - 控制反轉 (IOC) vs 依賴注入(DI)

控制反轉 Inversion of Control (IOC)

控制反轉是一種程式設計的方式。它的精神在於程式中所需要的輔助物件,並不是在自己的類別中建立,而是由外部控制的。建立好後,將其傳遞給主程式,這個動作稱為依賴注入,是控制反轉的實現方式。

  • 將 Object 的控制權交給外部的 Spring 容器來管理

依賴注入 dependency injection (DI)

實現控制反轉的方式,Spring 的設計目標之一是為了解隅,利用依賴抽象而非依賴實例的方式,因此設計了依賴注入(DI)。

  • @Component , @RestController @Service … 等註各類解加在 class 上,將 class 交給 Spring 管理,這些也就是在 Spring Boot 中所謂的 Bean (後面章節會有詳細介紹)
  • @Autowired 將被管理的 Bean 注入

應用範例

  • 舉例來說有個物件專門來處理寄送訊息叫做 Sender ,我們需要使用到這個物件就必須透過 new 方法來建立出來,假設今天原本的 Sender 有問題更改成別種 Sender ,就必須有建立這個物件的地方都需要修改一遍,這時候改成用 DI 的方式就會比較容易維護及更動。

沒有使用 DI 方式,使用 New 方法來建立物件並引用

1
2
3
4
5
6
7
8
9
10
11
public class Notify{
private Sender sender = new Sender();

// 如果需要更換方法,每個有用到的地方都要改
// private NewSender sender = new NewSender();

public void Send() {
sender.print("Hi! I'm Sean")
}
}

@Autowired 注入單一實做物件

將 Sender 交給 Spring 容器管理,使用 @Autowired 注入去使用,等於是透過 Spring 去建立物件出來

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class Notify{

// 如果要更改方法,往上註冊為 bean 的部分修改就可以,注入部分不需要改變
@Autowired
private Sender sender;

public void Send() {
sender.print("Hi! I'm Sean")
}
}

@Autowired 注入介面內含多個已實做物件

如果我們可能有多種 Sender 需要去實踐,可以用 interface 來建立一個介面去分成不同方法實踐

1
2
3
4
5
@Component
public interface Sender {
void send(String msg);
}

1
2
3
4
5
6
7
8
@Component
public class GoogleMailSender implements Sender {
@Override
public void send(String msg) {
System.out.println("內容: " + msg + " 我用 Google 郵件寄出了!!")
}
}

1
2
3
4
5
6
7
8
@Component
public class OutlookMailSender implements Sender {
@Override
public void send(String msg) {
System.out.println("內容: " + msg + " 我用 Outlook 郵件寄出了!!")
}
}

可以將 @Component 選擇注入其中一種,Spring 容器會自動幫我們選擇我們有注入的那個,但如果兩種都注入,就會出現錯誤,所以要再選擇用 @Qualifier(”bean 名稱”) 註解來指定要入住哪個

    • 注意注入容器的 bean 名稱開頭是小寫
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class Notify{

@Autowired
@Qualifer("GoogleMailSender") // @Qualifer("outlookMailSender")
private Sender sender;

public void Send() {
sender.print("Hi! I'm Sean")
}
}

總結:

利用 IOC 和 DI 可以有以下優點:

  • 鬆耦合

class 之間的鬆耦合,降低各個 class 之間的關聯性。

像是假設我們本來得在 Notify 中去指定要使用的是哪家廠牌的 MailSender(可能是 google, yahoo…) 而這就會讓 Notify 和 MailSender 的耦合變大,但使用了 Spring IoC 之後,不需要了解這個 Sender 是哪種,只要確定有被正確注入某種可以使用的 MailSender。

  • 統一生命週期管理

交由 Spring 容器來管理這些物件的生命週期,減少不必要的引用。

因為我們將 MailSender 這個 object 改成是交由 Spring 容器來管理,因此 Spring 就會負責物件的創建、初始化、以及銷毀,所以就不需要我們親自去處理這件事情。

  • 方便測試程式

可以方便注入至所需要測試的程式中

所有的 object 都是由外部的 Spring 容器來做管理,因此我們就可以輕鬆在測試程式內在測試的過程中,將 Spring 容器中的 object 給替換掉,容易的使用像是 Mock 的技術。


Ref: