零、前言
從第一天開始學習SpringBoot,到現在已經有兩年了,兩年以來我的搬磚能力不斷增強,但理論層面仍然是零基礎小白。
因此,打破瓶頸的方法,就是像學長們一樣,多讀書、多看理論知識,然後應用實踐。
所以我開始學習Spring的兩大特性之一——控制反轉(IoC)。
在此之前,我們需要了解什麼是依賴關係。
如果A的成功運行必須需要調用B,此時就可以稱為B是A的依賴。
舉個例子,Controller要想調用Service的方法,就必須有一個可以操作的Service對象的引用(指針),此時這個Controller就依賴這個Service。
一、常規情況下,依賴關係的對象是怎麼實例化的
最傳統的方式就是:直接new對象,用誰就new誰。
例如:
如果想完成一個學生管理的功能,需要StudentController(控制器)、StudentService(服務)以及StudentRepository(倉庫),而學生和班級是ManyToOne,因此可能會調用KlassService(班級服務)和KlassRepository(班級倉庫)
而在實現班級管理時,也是類似的依賴關係。
如圖所示:
如果學生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對象實現組件間依賴關係,但問題也很明顯。
侷限性
從業務邏輯上看,控制器、服務、倉庫,都是負責數據流的處理和傳遞,所以應該都是單例模式,重複的實例化這些對象除了消耗多餘資源以外,更重要的是會干擾內部分變量的正常調用。
所以在某些場合,完全可以讓全局共享同一個依賴關係的實例對象。
有人可能會説,那可以繼續改進一下,只在某個特定的組件中完成它的依賴關係調用,其他的組件共享這些對象。
但由於業務邏輯的不確定性,編寫時很難確定組件的生命週期,
誰來創建、何時創建、何時釋放、釋放時能否保證它已經不被調用了,這些都是問題。
此外還有一個問題,實際創建對象的時候並不是簡單的new一下,而應該通過構造函數注入一些屬性。
但如果兩個類相互依賴的時候,這樣的對象就new不出來了(先有雞先有蛋的問題)。
二、控制反轉(Inversion of Control)
控制反轉的字面意思是:本來維護對象的工作由開發者完成,所謂反轉,就是把這個過程交給程序自身完成。
開發者只需要告訴Spring對象之間的依賴關係即可,實現細節由Spring通過反射的方式自動完成。
人為規定,我們把通過Spring IoC創建的組件叫做Bean。
下面有三種依賴注入的方式:
① 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 {
...
}
// 第二步,聲明依賴關係的變量
StudentService studentService;
// 第三步,在依賴關係上加入@Autowired註解
@Autowired
StudentService studentService;
簡單來説,Spring為我們準備了一個容器,來放置這些依賴。
所謂容器,就是一個容納東西的物品,現實中的容器包括水杯、水壺、冰箱、整理箱等等。
Spring容器給這些對象提供了空間,在需要裝配的對象的時候,Spring會按照依賴關係,按順序實例化這些對象,並且放置到容器中。
這些容器中的對象,將會由Spring維護,並且遵循單例模式,也就是説,全局只有一個此類的實例化對象。
當其他對象也依賴容器裏已有的對象時,會直接提供容器內的引用(指針)。
三、總結
總之,IoC使對象的調用與對象關係的維護分離開來,用户在使用對象時,不必在手動實例化、手動裝配,也不再需要關注它的生命週期,只需要把精力集中在對象功能上即可。
此外,IoC在需要時通過單例模式,避免了冗餘的對象,降低資源消耗,也讓Beans可以共享,讓軟件執行的邏輯更加符合業務邏輯。
事實上,使用或不使用單例模式,還是根據具體業務需求來調整。
後記
曾經在考研之前離隊過一段時間,那時除了要複習以外,還有一個感覺就是,自己遇到了瓶頸,當時覺得,代碼能看懂,但是記不住,淺層的代碼語法和原理可以理解,但僅限於此,對於深層次的知識,不僅僅是不懂,更重要的是也不知道怎麼進一步的學習,所以水平長期停留在半瓶醋的狀態。
或許,這次突然意識到”學習理論是一門必修課“之後,開始有意識的去主動學習的時候,這個瓶頸期就度過了,至少心態平穩了許多,希望以後會有更多的進步吧。
這篇文章怎麼來的
準確的説,是我先學習廖雪峯老師的教程,然後結合自己的理解重新默寫了一遍,目的只是為了加深理解。
所以一定會有重複的內容,並非抄襲。
參考資料:
https://www.liaoxuefeng.com/w...