初識Spring IoC

LYX6666發表於2022-03-08

零、前言

從第一天開始學習SpringBoot,到現在已經有兩年了,兩年以來我的搬磚能力不斷增強,但理論層面仍然是零基礎小白。
因此,打破瓶頸的方法,就是像學長們一樣,多讀書、多看理論知識,然後應用實踐。

所以我開始學習Spring的兩大特性之一——控制反轉(IoC)

在此之前,我們需要了解什麼是依賴關係
如果A的成功執行必須需要呼叫B,此時就可以稱為B是A的依賴
image.png
舉個例子,Controller要想呼叫Service的方法,就必須有一個可以操作的Service物件的引用(指標),此時這個Controller就依賴這個Service。
image.png

一、常規情況下,依賴關係的物件是怎麼例項化的

最傳統的方式就是:直接new物件,用誰就new誰。
例如:
如果想完成一個學生管理的功能,需要StudentController(控制器)、StudentService(服務)以及StudentRepository(倉庫),而學生和班級是ManyToOne,因此可能會呼叫KlassService(班級服務)和KlassRepository(班級倉庫)
而在實現班級管理時,也是類似的依賴關係。
如圖所示:
image.png
如果學生Service想檢視學生資訊,就要:

//  例項化學生倉庫
private StudentRepository studentRepository = new StudentRepository();
// 例項化班級服務和班級倉庫,從而實現關聯查詢
private KlassService klassService = new KlassService();
private KlassRepository klassRepository = KlassRepository();

並且,如果班級Service想檢視班級資訊,就要:

//  例項化班級倉庫
private KlassRepository klassRepository = KlassRepository();
// 例項化學生服務和學生倉庫,從而檢視每個班級裡的學生
StudentService studentService = new StudentService();
private StudentRepository studentRepository = new StudentRepository();

此外,C層呼叫Service時,也需要例項化它:

StudentService studentService = new StudentService();

這樣一來,確實可以通過new物件實現元件間依賴關係,但問題也很明顯。

侷限性

從業務邏輯上看,控制器、服務、倉庫,都是負責資料流的處理和傳遞,所以應該都是單例模式,重複的例項化這些物件除了消耗多餘資源以外,更重要的是會干擾內部分變數的正常呼叫。
所以完全可以讓全域性共享同一個依賴關係的例項物件。

有人可能會說,那可以繼續改進一下,只在某個特定的元件中完成它的依賴關係呼叫,其他的元件共享這些物件。
但由於業務邏輯的不確定性,編寫時很難確定元件的生命週期,
誰來建立、何時建立、何時釋放、釋放時能否保證它已經不被呼叫了,這些都是問題。

二、控制反轉(Inversion of Control)

控制反轉的字面意思是:本來維護物件的工作由開發者完成,所謂反轉,就是把這個過程交給程式自身完成。
開發者只需要告訴Spring物件之間的依賴關係即可,實現細節由Spring通過反射的方式自動完成。
人為規定,我們把通過Spring IoC建立的元件叫做Bean。
image.png

下面有三種依賴注入的方式:

① set方法注入

例如StudentController中注入StudentService

// 第一步,在類中宣告依賴關係
StudentService studentService;

// 第二步,在類中宣告一個set方法
public void setStudentService(StudentService studentService) {
    this.studentService= studentService;
}

// 第三步,在xml中”告訴“Spring如何依賴
<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="StudentController" class="test.lyx6666.controller.StudentController">
        <property name="studentService" ref="studentService" />
    </bean>

    <bean id="studentService" class="test.lyx6666.controller.studentService" />
</beans>

但這種方式還是比較麻煩,筆者才疏學淺,沒動手寫過,只在教程裡見過。

② 建構函式注入

// 第一步,宣告這個類是Spring元件,例如@RestController或@Service等
@RestController
@RequestMapping("student")
public class StudentController {
    ...
}

// 第二步,宣告依賴關係的變數,必須為  private final 
private final StudentService studentService;

// 第三步,在建構函式中宣告想要注入的變數
public StudentController(StudentService studentService) {
  this.studentService = studentService;
}

③ @Autowired注入

// 第一步,宣告這個類是Spring元件,例如@RestController或@Service等
@RestController
@RequestMapping("student")
public class StudentController {
    ...
}

// 第二步,宣告依賴關係的變數,必須為  private final 
private final StudentService studentService;

// 第三步,在依賴關係上加入@Autowired註解
@Autowired
private final StudentService studentService;

簡單來說,Spring為我們準備了一個容器,來放置這些依賴。
所謂容器,就是一個容納東西的物品,現實中的容器包括水杯、水壺、冰箱、整理箱等等。
image.png

Spring容器給這些物件提供了空間,在需要裝配的物件的時候,Spring會按照依賴關係,按順序例項化這些物件,並且放置到容器中。
image.png
這些容器中的物件,將會由Spring維護,並且遵循單例模式,也就是說,全域性只有一個此類的例項化物件。
當其他物件也依賴容器裡已有的物件時,會直接提供容器內的引用(指標)。

三、總結

總之,IoC使物件的呼叫與物件關係的維護分離開來,使用者在使用物件時,不必在手動例項化、手動裝配,也不再需要關注它的生命週期,只需要把精力集中在物件功能上即可。
此外,IoC通過單例模式,避免了冗餘的物件,降低資源消耗,也讓Beans可以共享,讓軟體執行的邏輯更加符合業務邏輯。

後記

曾經在考研之前離隊過一段時間,那時除了要複習以外,還有一個感覺就是,自己遇到了瓶頸,當時覺得,程式碼能看懂,但是記不住,淺層的程式碼語法和原理可以理解,但僅限於此,對於深層次的知識,不僅僅是不懂,更重要的是也不知道怎麼進一步的學習,所以水平長期停留在半瓶醋的狀態。
或許,這次突然意識到”學習理論是一門必修課“之後,開始有意識的去主動學習的時候,這個瓶頸期就度過了,至少心態平穩了許多,希望以後會有更多的進步吧。

這篇文章怎麼來的

準確的說,是我先學習廖雪峰老師的教程,然後結合自己的理解重新默寫了一遍,目的只是為了加深理解。
所以一定會有重複的內容,並非抄襲。

參考資料:

https://www.liaoxuefeng.com/w...

相關文章