初學Spring不太容易理解的問題
初學Spring不太容易理解的問題
-
Q: 控制反轉(IoC)是什麼?到底是誰的控制被反轉了?
A: Ioc是一個容器,在Spring中,它會認為一切Java資源都是Java Bean,容器的目標就是管理這些Bean和它們之間的關係。所以在Spring IoC裡面裝載的各種Bean,也可以理角為Java的各種資源,包括Java Bean的建立、事件、行為等,它們由IoC容器管理。也就是說建立物件、處理物件之間的關係、物件的生命週期等等就不需要我們去管了,把這些工作都交由Spring IoC去做,我們需要做的只是告訴IoC我們需要什麼IoC就會給你,本來由我們來控制的物件的建立活動轉到了IoC身上,所以說控制被反轉了。
-
Q: 依賴注入(DI)是什麼?注入了什麼?注到哪裡了?
A: 依賴注入其實說白了就是給物件賦值。如果一個物件需要另一個物件協助時,無須在程式碼中建立被呼叫者,而是依賴外部的注入。比如我要擰螺絲,就需要依賴螺絲刀,依賴注入就是把被依賴的螺絲刀送到我手上來。依賴注入通常有兩種:setter注入和構造器注入。簡單點說setter注入就是setter是先通過無參構造器new一個空物件,再通過setter方法把值賦給這個空物件;而構造器注入就是根據相應的構造方法的引數傳入對應的值來new一個物件。所以依賴注入就是將值(或物件)注入到目標物件中。
構造注入:
Screwdriver screwdriver = new Screwdriver(); //建立螺絲刀 Person person = new Person(screwdriver) //把螺絲刀給Person物件
setter注入:
Screwdriver screwdriver = new Screwdriver(); Person person = new Person(); person.setScrewdriver(screwdriver); //把螺絲刀給person物件
-
Q:控制反轉(IoC)和依賴注入(DI)的關係是什麼?
A: 依賴注入是控制反轉的一種實現方式。就是說雖然建立物件的工作歸IoC容器管了,但容器還是得遵守學生守則的。想要建立物件就要通過構造方法,至於是在建立物件時將值傳入(構造器注入)還是通過空構造方法建立物件後再通set方法傳入(setter注入)那就是看你的喜好了。
-
Q: Spring中所說的Bean到底是什麼,有什麼用?
A: Spring容器就像一個大工廠,工廠根據人們所指定的方式(配置)生產產品(bean),這些產品可以實現某個具體的功能。當 然這些產品也並不是非要Spring工廠來生產不可,也可以由我們自己生產,就如我們想喝果汁,可以自己種各種疏果,然後購買榨汁機榨成果汁,最後根據自己的口味加點糖和冰。但其實我們只是想喝一杯果汁而己。在Spring中Bean就像是果汁,是一個有具體功能的物件,對於一些手動建立比較麻煩的物件,我們大可交給Spring去完成建立。
-
Q: 為什麼要用Spring IoC容器來管理Bean?直接new一個不行嗎?
A: 要知道在一個專案中一個物件往往不是孤立的,物件之間相互作用,相互協作,或者相互依賴。一個典型的相互依賴的例子是物件A中有屬性B,要使用A的就需要先建立B的例項,而如果此時B又依賴C,C又依賴D呢?而在Spring中這些工作都是由Spring容器來完成的,使用時只要從Spring容器中“拿”物件A就行了。就像前面所說的喝果汁的故事,果汁物件需要糖物件和冰物件,而在榨出果汁之前還得先建立榨汁機物件,在這之前甚至需要疏果物件…在開發中程式設計師可能注意力都集中在如何去種植疏果了。
Juice juice = (Juice) context.getBean("juice");
-
Q:面向切面是什麼意思?切面是什麼?
A: 在軟體開發中,散佈於應用中多處的功能被稱為橫切關注點(cross-cutting concern)。通常來講,這些橫切關注點從概念上是與應用的業務邏輯相分離的(但是往往會直接嵌入到應用的業務邏輯之中)。把這些橫切關注點與業務邏輯相分離正是面向切面程式設計(AOP)所要解決的問題。比如在一個業務流程中,完成流程流程3之前需要完成一些操作(前置通知),完成流程3之後需要完成一些操作(後置通知),那麼可以把之前之後需要做的操作封裝成一個切面,只需在切點宣告一個切面Spring就能自動執行這些操作。
比如,沒有切面的程式碼是這樣的//業務程式碼 service.process1(); service.process2(); processBeforeProcess3_1(); processBeforeProcess3_2(); processBeforeProcess3_3(); service.process3(); processAfterProcess3_1(); processAfterProcess3_2(); processAfterProcess3_3(); service.process4(); service.process5();
面向切面的程式碼
//定義一個切面 class Aspect{ //定義切點 @Pointcut("切點是service.process3()") public void pointCut() //service.process3()之前的操作 @Before public void before(){ processBeforeProcess3_1(); processBeforeProcess3_2(); processBeforeProcess3_3(); } //service.process3()之後的操作 @After public void after(){ processAfterProcess3_1(); processAfterProcess3_2(); processAfterProcess3_3(); } }
//業務程式碼 service.process1(); service.process2(); service.process3(); service.process4(); service.process5();
可以看到,面向切面方法的程式碼並沒有變少,反而多定義了一個類,但是在業務程式碼中我們只需要關注真正的業務流程,而不會被其它雜事所幹擾,
service.process3
甚至都不知道自己與service.process2()
和service.process4()
之間還執行了這麼多操作。
一個典型的例子就是進行資料庫操作,在進行真正的增刪查改操作之前,需要載入驅動,然後獲取一個連線,再定義一個SQL查詢物件。資料庫操作完成後還需要進行提交(如果需要用到事物的話),然後在catch
語句中寫滾語句和寫入日誌,最後還得在finally
中關閉資源,關閉資源前甚至還要檢查物件是否為null
。與真正進行資料庫操作的其它步驟其實都可以放入before和after中,再宣告需要此切面的切點即可。