為什麼要使用Spring IOC?
思考
Spring已經佔據我們Java開發框架中的半壁江山了,從一開始工作我們就在使用Spring。但是到底為什麼要用Spring,可能很多人都沒有去思考過這個問題?許多人可能也疲於應對需求,無暇思考這種看似理所當然的問題。那今天,我們就好好來討論一下究竟為什麼要使用Spring IOC?
逆向思考
假設在最初沒有Spring IOC這種框架的時候,我們採用傳統MVC的方式來開發一段常見的使用者邏輯。
使用者DAO
public class UserDAO {
private String database;
public UserDAO(String dataBase) {
this.database = dataBase;
}
public void doSomething () {
System.out.println("儲存使用者!");
}
}
複製程式碼
使用者Service
public class UserService {
private UserDAO dao;
public UserService(UserDAO dao) {
this.dao = dao;
}
public void doSomething() {
dao.doSomething();
}
}
複製程式碼
使用者Controller
public class Controller {
public UserService service;
public Controller(UserService userService) {
this.service = userService;
}
public void doSomething () {
service.doSomething();
}
}
複製程式碼
接下來我們就必須手動一個一個建立物件,並將dao、service、controller依次組裝起來,然後才能呼叫。
public static void main(String[] args) {
UserDAO dao = new UserDAO("mysql");
UserService service = new UserService(dao);
Controller controller = new Controller(service);
controller.doSomething();
}
複製程式碼
分析一下這種做法的弊端有哪些呢?
- 在生成Controller的地方我們都必須先建立dao再建立service最後再建立Controller,這麼一條繁瑣的建立過程。
- 在這三層結構當中,上層都需要知道下層是如何建立的,上層必須自己建立下層,這樣就形成了緊密耦合。為什麼業務程式設計師在寫業務的時候卻需要知道資料庫的密碼並自己建立dao呢?不僅如此,當如果dao的資料庫密碼變化了,在每一處生成Controller的地方都需要進行修改。
- 通過new關鍵字生成了具體的物件,這是一種硬編碼的方式,違反了面向介面程式設計的原則。當有一天我們從Hibernate更換到Mybatis的時候,在每一處new DAO的地方,我們都需要進行更換。
- 我們頻繁的建立物件,浪費了資源。
這時候我們再來看看如果用SpringIOC的情況,剛才的程式碼變成如下。
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
Controller controller = (Controller) context.getBean("controller");
controller.doSomething();
}
複製程式碼
很明顯,使用IOC之後,我們只管向容器索取所需的Bean即可。IOC便解決了以下的痛點:
- Bean之間的解耦,這種解耦體現在我們沒有在程式碼中去硬編碼bean之間的依賴。(不通過new操作依次構建物件,由springIOC內部幫助我們實現了依賴注入)。一方面,IOC容器將通過new物件設定依賴的方式轉變為執行期動態的進行設定依賴。
- IOC容器天然地給我們提供了單例。
- 當需要更換dao的時候,我們只需要在配置檔案中更換dao的實現類,完全不會破壞到之前的程式碼。
- 上層現在不需要知道下層是如何建立的。
通過這個例子可能讀者有點若有所思了,那我們再來看一下IOC的定義是什麼。
定義
控制反轉(Inversion of Control,縮寫為IoC),是面向物件程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查詢”(Dependency Lookup)。通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體將其所依賴的物件的引用傳遞給它。也可以說,依賴被注入到物件中。
解讀
控制反轉,控制體現在什麼地方?控制就體現在我們一開始的例子,上層需要去控制new下層物件。而反轉意味著什麼呢? 反轉意味著我們把物件建立的控制權交出去,交給誰呢?交給IOC容器,由IOC容器負責建立特定物件,並填充到各個宣告需要特定物件的地方。同學們可能會對IOC和DI覺得很繞,其實IOC相當於介面,它規定了要實現什麼?而依賴注入(DI)就是具體的實現,它實現了IOC想要的效果。IOC的思想很好地體現了面向物件設計法則之一——好萊塢法則:“別找我們,我們找你”;即由IoC容器幫物件找相應的依賴物件並注入,而不是由物件主動去找。
如何減低耦合?我們舉個例子,當在IDEA開發環境中我們新增外掛的時候,卻需要去知道該外掛需要什麼依賴,要怎麼才能成功開啟外掛?是不是對使用者相當不友好呢?既然我們要使用外掛,意味著我們和外掛之間是耦合的,無法避免。但是如果我們還需要知道外掛需要的依賴和開啟外掛的步驟,說明我們和外掛之間的耦合關係更強烈了。所以SpringIOC的定義當中寫道“降低耦合”而非“消除耦合”,但只要耦合度降低的話,就有利於程式碼的維護和擴充套件。思考一下?是不是跟Maven的機制很類似呢?在Maven中宣告依賴的話,Maven的自動將該依賴所需的其他依賴遞迴的尋找。你可以把你自己想象成一個物件的設計師,你設計了一張物件內部構造的圖紙,交給SpringIOC,它會自動地根據這份圖紙生成這個物件。
IOC容器實現了開發中物件粒度的一種元件化,將每個物件當做元件一樣放進容器當中。而每個元件都是可插拔,這種是最佳的物件解耦方式。一方面通過將物件之間的耦合關係從編譯期推遲到執行期。一旦有需要這個物件的話,就直接使用,客戶程式設計師完全不需要知道這個物件的來龍去脈。並且IOC容器對Bean的統一管理使得AOP的實現更加的方便。這樣一來,我們客戶程式設計師就可以更專注在業務程式碼的編寫上。