IoC 是 Inversion of Control 的簡寫,譯為“ 控制反轉 ”,它不是一門技術,而是一種設計思想,是一個重要的面向對象編程法則,能夠指導我們如何設計出 松耦合、更優良的程序。
Spring 通過 IoC 容器來管理所有 Java 對象的實例化和初始化,控制對象與對象之間的依賴關系。將由 IoC 容器管理的 Java 對象稱為 Spring Bean,它與使用關鍵字 new 創建的 Java 對象沒有任何區別。
IoC 容器是 Spring 框架中最重要的核心組件之一,它貫穿了 Spring 從誕生到成長的整個過程。
IOC容器
1.控制反轉,依賴注入
控制反轉(Inversion of Control,簡稱IoC)是一種軟件設計原則,它將傳統的程序控制流程反轉過來,即由被調用者控制調用者的執行過程。這樣做的好處在于降低了代碼之間的耦合性,使得代碼更加靈活、可維護和可擴展。
一個典型的控制反轉例子是使用依賴注入(Dependency Injection,簡稱DI)。依賴注入是控制反轉的一種實現方式,它通過將依賴關系從代碼內部移動到外部來實現解耦。
假設我們有一個簡單的應用程序,其中包含一個服務類 MessageService 負責發送消息,并且這個服務類依賴于一個用于實際發送消息的 MessageSender 接口。在傳統的實現中,MessageService 通常會直接創建一個特定的 MessageSender 實例,但在使用控制反轉的思想下,我們會將 MessageSender 的創建和注入從 MessageService 中分離出來。
傳統實現(無控制反轉):
public class MessageService {private MessageSender messageSender;public MessageService() {// 在構造函數中直接實例化特定的 MessageSender 實例this.messageSender = new EmailMessageSender(); // 假設使用 EmailMessageSender 發送消息}public void sendMessage(String message) {messageSender.send(message);}
}
使用控制反轉(依賴注入):
public class MessageService {private MessageSender messageSender;public MessageService(MessageSender messageSender) {// 通過構造函數接收一個 MessageSender 實例,由外部注入this.messageSender = messageSender;}public void sendMessage(String message) {messageSender.send(message);}
}
現在,使用控制反轉,我們將 MessageSender 的實例化過程交由調用者來完成。這樣做的好處是,在應用程序的其他地方,我們可以根據需要輕松地更換不同的 MessageSender 實現(例如,使用短信發送消息而不是電子郵件),而無需修改 MessageService 的代碼。同時,這也使得單元測試變得更容易,因為我們可以輕松地注入一個模擬的 MessageSender 實例來進行測試。
舉例:
如果你希望在使用短信發送消息而不是電子郵件時進行更改,只需更改 MessageService 類的構造函數中注入的 MessageSender 實例即可。這就是控制反轉的好處,它使得我們可以在應用程序的其他地方配置不同的實現,并且只需更改依賴注入的部分,而不需要修改 MessageService 的實現代碼。
讓我們看一下如何在 MessageService 中使用短信發送消息的例子:
- 創建一個用于發送短信的實現類 SmsMessageSender:
public class SmsMessageSender implements MessageSender {public void send(String message) {// 實現發送短信的邏輯System.out.println("Sending SMS: " + message);}
}
- 現在,在使用 MessageService 的地方,通過構造函數將 SmsMessageSender 注入:
public class MyApp {public static void main(String[] args) {// 使用短信發送消息MessageSender smsSender = new SmsMessageSender();MessageService messageService = new MessageService(smsSender);// 發送消息messageService.sendMessage("Hello, this is a text message!");}
}
通過這樣的更改,MessageService 現在使用 SmsMessageSender 來發送消息而不是之前的 EmailMessageSender,而且這個更改只發生在應用程序的一個地方,使得代碼更易于維護和擴展。同時,你可以根據需要在其他地方繼續使用 EmailMessageSender,并且無需對 MessageService 的實現進行任何修改。
set注入和依賴注入
在依賴注入(Dependency Injection,DI)的實踐中,有兩種常見的注入方式:構造函數注入(Constructor Injection)和屬性/方法注入(Setter Injection)。
構造函數注入(Constructor Injection):
構造函數注入是將依賴通過類的構造函數傳遞進來的方式。在這種注入方式中,類的依賴關系在創建對象時就被明確地傳遞給對象,保證了對象在被實例化后就具備了所需的依賴。這種注入方式通常被認為是推薦的注入方式,因為它在對象創建過程中就明確了依賴關系,使得對象在創建后處于一種可用的狀態。
示例:構造函數注入的Java代碼
public class MessageService {private MessageSender messageSender;public MessageService(MessageSender messageSender) {this.messageSender = messageSender;}// 業務邏輯方法使用依賴發送消息public void sendMessage(String message) {messageSender.send(message);}
}
屬性/方法注入(Setter Injection):
屬性/方法注入是通過setter方法或者普通方法將依賴傳遞給對象的方式。在這種注入方式中,對象首先由默認構造函數創建,然后通過setter方法或者普通方法設置其依賴。由于依賴在創建對象后才被注入,因此在對象實例化時,它可能處于不完整或不可用的狀態,這是它與構造函數注入的主要區別。
示例:屬性/方法注入的Java代碼
public class MessageService {private MessageSender messageSender;// 通過setter方法注入依賴public void setMessageSender(MessageSender messageSender) {this.messageSender = messageSender;}// 業務邏輯方法使用依賴發送消息public void sendMessage(String message) {messageSender.send(message);}
}
需要注意的是,屬性/方法注入可能導致對象處于不完整或不可用的狀態,因此需要在使用對象之前確保其依賴已經被正確注入。另一方面,構造函數注入在對象創建時就明確了依賴關系,因此更容易維護和確保對象的完整性。
通常情況下,如果可能,推薦使用構造函數注入,因為它更符合依賴注入的原則,能夠在對象創建時就將依賴傳遞進來,減少了對象的不穩定性。而屬性/方法注入則適用于一些特殊情況,例如使用框架進行依賴注入時可能更為便利,或者在某些場景下靈活性要求較高時使用。
2.IoC容器在Spring的實現
Spring 的 IoC 容器就是 IoC思想的一個落地的產品實現。IoC容器中管理的組件也叫做 bean。在創建 bean 之前,首先需要創建IoC 容器。Spring 提供了IoC 容器的兩種實現方式:
①BeanFactory
這是 IoC 容器的基本實現,是 Spring 內部使用的接口。面向 Spring 本身,不提供給開發人員使用。
②ApplicationContext
BeanFactory 的子接口,提供了更多高級特性。面向 Spring 的使用者,幾乎所有場合都使用 ApplicationContext 而不是底層的 BeanFactory。
③ApplicationContext的主要實現類
類型名 | 簡介 |
---|---|
ClassPathXmlApplicationContext | 通過讀取類路徑下的 XML 格式的配置文件創建 IOC 容器對象 |
FileSystemXmlApplicationContext | 通過文件系統路徑讀取 XML 格式的配置文件創建 IOC 容器對象 |
ConfigurableApplicationContext | ApplicationContext 的子接口,包含一些擴展方法 refresh() 和 close() ,讓 ApplicationContext 具有啟動、關閉和刷新上下文的能力。 |
WebApplicationContext | 專門為 Web 應用準備,基于 Web 環境創建 IOC 容器對象,并將對象引入存入 ServletContext 域中。 |