哈囉大家好,我是古古
在上一篇文章中,我們有成功的使用了 IntelliJ 這套軟體,去創建出了第一個 Spring Boot 程式,先對如何撰寫 Spring Boot 程式有了一個最基本的了解
那麼從這篇文章開始,我們就會開始來介紹 Spring 框架中的一個非常重要的特性,也就是 IoC,所以我們就開始吧!
IoC 的全稱是 Inversion of Control,中文翻譯成「控制反轉」,那這個控制是控制什麼,反轉又是反轉什麼,我們可以直接透過一個印表機的例子來了解一下
假設今天我們先設計一個印表機 Printer 的 interface,這個印表機裡面只有一個方法,就是 print()
方法印東西,程式如下:
public interface Printer {
void print(String message);
}
有了 interface 之後,我們就可以去寫一個 class 來實作這個 Printer interface,所以我們可以寫一個 HpPrinter 的 class,表示這是一個 HP 牌子的印表機,程式如下:
public class HpPrinter implements Printer {
@Override
public void print(String message) {
System.out.println("HP印表機: " + message);
}
}
並且我們也可以再寫一個 CanonPrinter 的 class,表示這是一個 Canon 牌子的印表機,程式如下:
public class CanonPrinter implements Printer {
@Override
public void print(String message) {
System.out.println("Canon印表機: " + message);
}
}
所以到目前為止,我們就有了 2 個 class,一個是 HpPrinter、另一個則是 CanonPrinter,而這兩個印表機裡面,就都會去 implements Printer 這個 interface,去實作他裡面的 print()
方法
有了兩個印表機之後,我們可以再去設計一個 Teacher 的 class,然後在這個 Teacher 的 class 裡面宣告一個 Printer 類型的變數,並且在後面去 new 一個 HpPrinter 出來
補充:這裡使用到 Java 中的多型 (polymorphism) 的概念,因此 HpPrinter 才可以被向上轉型成 Printer 類型
public class Teacher {
private Printer printer = new HpPrinter();
public void teach() {
printer.print("I'm a teacher");
}
}
所以到這裡為止,整個結構圖就會長的像是下圖這個樣子,剛剛我們所新增的 Teacher class,就可以在他裡面去宣告一個 Printer 的變數,然後在裡面去使用了 HpPrinter 印表機去印東西了
假設這個時候 HpPrinter 壞掉了,因此 Teacher 這個 class 想要改成是去使用 CanonPrinter 這個印表機,繼續去印東西出來
那這個時候,我們就只能去改寫 Teacher 裡面的程式,將裡面的 Printer 變數,改成是去 new 一個 CanonPrinter 出來,因此程式就會長得像是下面這個樣子
那到這邊,我們可能就會產生了一個新的想法:就是身為一個 Teacher,我們其實只是想要一台印表機去印東西,不論這個印表機是 HP 牌的還是 Canon 牌的,只要他可以印出來就好了,我們根本不在意所使用的是哪個牌子的印表機
但是因為我們在 Teacher 的 Java 程式裡面,具體的去指定了要使用的是哪一牌的印表機,所以每當我們換一次牌子,我們就必須要把有使用到印表機的地方全部修改一遍,那現在只有 Teacher 這個 class 需要被修改所以大家可能覺得還好,大不了就動手改一下,但是當你的 project 越寫越大之後,要這樣一個一個修改是非常困難的
所以這時候,Spring 就提出了一個新的概念來解決這個問題,也就是 IoC(Inversion of Control,控制反轉)
為了解決上面的替換印表機的問題,Spring 就提出了一個新的想法,也就是:「將這個 Printer 的 object 交由 Spring 保管,當誰要使用印表機的時候,再去跟 Spring 拿就好」,反正我們作為 Teacher 來說,也不是很在意使用到的是 HP 牌的印表機還是 Canon 牌的印表機,我們只要能拿到一個印表機來印東西就好
所以在這個情境下,HpPrinter 和 CanonPrinter 這兩個印表機就統一交由 Spring 進行保管,而我們作為 Teacher,不需要提前將 HpPrinter 和 CanonPrinter 的資訊寫死在 Java 裡面,所以 Teacher class 就可以改寫成像是這樣:
可以看到在 Teacher class 裡面,我們只定義了一個 Printer 的變數,但是我們並沒有為這個 Printer 去 new 一個 HpPrinter 或是 CanonPrinter,因此這個 Printer 變數的值就會是 null
但是當程式運作起來之後,Spring 就會去啟動一個 Spring Container(又稱為 Spring 容器),然後 Spring 會預先去 new 一台印表機出來(此處以 HpPrinter 當作例子),並且存放在 Spring 容器裡面保存
然後當 Teacher 想要使用印表機時,Spring 就會把這個預先 new 好、並且存放在 Spring 容器中的 HpPrinter 交給 Teacher,因此 Teacher 就可以正常的使用這個印表機去印東西了
所以大家也可以簡單的想像成是:Spring 他會預先去買一台印表機,然後儲存在「Spring 容器」裡面,而誰想要印東西,他就去跟 Spring 借,所以印東西的人就不用自己去準備一台印表機,只要一直去跟 Spring 借就好了,這個就是「Spring IoC」的概念了!!
透過上面這個例子大概了解 Spring IoC 的概念之後,我們可以回頭再看一下 IoC 的定義
IoC 的全稱是 Inversion of Control,中文是翻譯為控制反轉,而 IoC 的概念,就是「將 object 的控制權交給了外部的 Spring 容器來管理」,所謂的 Control (控制),就是對於 object 的控制權
像是以前我們在寫 Java 程式時,就是會在每個 class 中去 new 一個 HpPrinter 出來,因此這個 HpPrinter 的控制權就是在自己(Teacher class)手上(如下圖左)
而當我們用了 Spring 框架之後,則是會由 Spring 容器統一去 new 一個 HpPrinter 出來供大家使用,因此 HpPrinter 的控制權,就是在外部容器(Spring 容器)手上(如下圖右)
也因為我們 將 HpPrinter 的「控制權」,從自己手上「轉移到」外部的 Spring 容器,由外部的 Spring 容器統一去做管理,所以就稱為是 Inversion of Control,簡稱為 IoC
了解了 IoC 的定義之後,接著我們可以來看一些使用 Spring IoC 的優點
使用 Spring IoC 來管理 object 基本上有三大好處,分別是:
第一個使用 Spring IoC 的優點,是可以達到 class 之間的鬆耦合,即是可以降低各個 class 之間的關聯性
像是我們本來得在 Teacher 中去指定要使用的是 HpPrinter 印表機,而這就會讓 Teacher 和 HpPrinter 的連結性變大,但是當我們改成使用了 Spring IoC 之後,Teacher 就只要直接拿印表機去做事就好,不需要了解這個印表機到底是 HP 牌子的還是 Canon 牌子的
因此使用 Spring IoC 的第一個好處,就是可以讓 class 之間的關聯性變低
使用 Spring IoC 的第二個優點,則是統一的生命週期管理
因為我們將 HpPrinter 這個 object 改成是交由 Spring 容器來管理,因此 Spring 就會負責 HpPrinter 的創建、初始化、以及銷毀,所以就不需要我們親自去處理這件事情
使用 Spring IoC 的第三個優點,即是更加方便的測試程式
因為所有的 object 都是由外部的 Spring 容器來做管理,因此我們就可以容易的使用 Mock 的技術,在測試的過程中,將 Spring 容器中的 object 給替換掉
補充:有關單元測試和 Mock 的部分,在此系列文中不會特別介紹到,有興趣的話可以參考另一篇文章的介紹: SpringBoot - 單元測試工具 Mockito
這篇文章先透過例子介紹了 Spring IoC 的原理,並且也一併介紹了使用 Spring IoC 的優點,讓大家可以透過印表機的例子,更好的去理解 Spring IoC 的概念
那麼下一篇文章,就會補充介紹 Spring IoC 的另外兩個重要的名詞:DI 和 Bean,讓大家更全面的了解 Spring IoC,那我們就下一篇文章見啦!