知識庫 / Spring RSS 訂閱

控制反轉和依賴注入 Spring 入門

Spring
HongKong
11
02:42 PM · Dec 06 ,2025

1. 概述

本教程將介紹 IoC(控制反轉)和 DI(依賴注入)的概念,並探討這些概念在 Spring 框架中的實現方式。

2. 控制反轉是什麼?控制反轉是軟件工程中的一個原則,它將對象的控制權或程序的某些部分轉移給容器或框架。我們通常在面向對象編程的背景下使用它。

與傳統的編程方式不同,在傳統的編程中,我們的自定義代碼會調用庫,IoC 允許框架控制程序的流程並調用我們的自定義代碼。為了實現這一點,框架使用帶有內置額外行為的抽象。 如果我們想要添加自己的行為,則需要擴展框架的類或插件自己的類。

這種架構的優點是:

  • 將任務的執行與它的實現解耦
  • 使其更容易在不同的實現之間切換
  • 程序的模塊化程度更高
  • 更容易通過隔離組件或模擬其依賴項來測試程序,並允許組件通過合同進行通信

我們可以通過諸如策略設計模式、服務定位器模式、工廠模式和依賴注入(DI)等機制來實現控制反轉。

接下來,我們將研究 DI。

3. 什麼是依賴注入?依賴注入是一種我們可以用來實現 IoC 的模式,其中控制權被反轉時,會設置對象的依賴項。

通過其他對象與對象建立連接,或者“注入”對象到其他對象中,是由彙編器完成的,而不是由對象本身完成。

在傳統編程中,我們創建對象依賴的方式如下所示:

public class Store {
    private Item item;
 
    public Store() {
        item = new ItemImpl1();    
    }
}

在上面的示例中,我們需要在 Store 類本身中實例化 Item 接口的實現。

通過使用依賴注入(DI),我們可以重寫示例,而無需指定我們想要使用的 Item 的實現。

public class Store {
    private Item item;
    public Store(Item item) {
        this.item = item;
    }
}

在接下來的部分,我們將探討如何通過元數據提供 Item 的實現。

依賴注入 (IoC) 和控制反轉 (DI) 都是相對簡單的概念,但它們對我們系統架構有着深遠的影響,因此完全理解它們非常值得。

4. Spring IoC 容器

IoC 容器是實現 IoC 的框架的常見特徵。

在 Spring 框架中,接口 ApplicationContext 代表 IoC 容器。 Spring 容器負責實例化、配置和組裝稱為 bean 的對象,以及管理它們的生命週期。

Spring 框架提供了多個 ApplicationContext 接口的實現:AnnotationConfigApplicationContext、ClassPathXmlApplicationContextFileSystemXmlApplicationContext 用於獨立應用程序,以及 WebApplicationContext 用於 Web 應用程序。

為了組裝 bean,容器使用配置元數據,該元數據可以是 XML 配置或註解的形式。

以下是手動實例化容器的一種方式:

ApplicationContext context
  = new ClassPathXmlApplicationContext("applicationContext.xml");

以下是翻譯後的內容:

以下是一個手動實例化容器的示例,使用了 AnnotationConfigApplicationContext

AnnotationConfigApplicationContext annotationContext = new AnnotationConfigApplicationContext();

當您創建 AnnotationConfigApplicationContext 實例並提供一個或多個配置類時,它會掃描這些類以查找 @Bean 註解和其他相關注解。然後,它會初始化和管理這些類中定義的 Bean,設置它們的依賴關係並管理它們的生命週期。您可以在這裏找到詳細示例。

要設置上文示例中的 item 屬性,可以使用元數據。然後容器將讀取此元數據並使用它在運行時組裝 Bean。

在 Spring 中,依賴注入可以通過構造函數、設置器或字段進行。

5. 基於構造函數的依賴注入在基於構造函數的依賴注入的方案中,容器將通過調用構造函數,並傳入代表我們想要設置的依賴項的參數來完成。

Spring 主要通過類型,然後是屬性名稱,最後是索引來解決每個參數,以消除歧義。下面我們通過註解來配置一個 Bean 以及它的依賴項:

@Configuration
public class AppConfig {

    @Bean
    public Item item1() {
        return new ItemImpl1();
    }

    @Bean
    public Store store() {
        return new Store(item1());
    }
}

@Configuration 註解表明該類是 Bean 定義的來源。我們也可以將其添加到多個配置類中。

我們使用@Bean註解在一個方法上定義一個 Bean。如果未指定自定義名稱,則 Bean 名稱將默認為方法名稱。

對於具有默認singleton作用域的 Bean,Spring 首先檢查 Bean 緩存中是否已存在實例,並在不存在時才創建新的實例。如果使用prototype作用域,則容器在每次方法調用時返回一個新的 Bean 實例。

通過 XML 配置創建 Bean 的另一種方式是:

<bean id="item1" class="org.baeldung.store.ItemImpl1" /> 
<bean id="store" class="org.baeldung.store.Store"> 
    <constructor-arg type="ItemImpl1" index="0" name="item" ref="item1" /> 
</bean>

6. 基於設置器依賴注入

對於基於設置器依賴注入,容器會在調用無參數構造函數或無參數靜態工廠方法實例化 Bean 後,調用我們 Bean 的設置器方法。 讓我們使用註解來配置這個:

@Bean
public Store store() {
    Store store = new Store();
    store.setItem(item1());
    return store;
}

我們還可以使用XML來實現相同的Bean配置:

<bean id="store" class="org.baeldung.store.Store">
    <property name="item" ref="item1" />
</bean>

我們可以將基於構造器和基於設置器的注入方式應用於同一個 Bean。Spring 文檔建議,對於強制依賴,應使用基於構造器的注入;對於可選依賴,則應使用基於設置器的注入。

7. 基於字段的依賴注入

在基於字段的依賴注入的情況下,我們可以通過使用 @Autowired 註解來標記依賴項進行注入:

public class Store {
    @Autowired
    private Item item; 
}

在構造 Store 對象時,如果不存在構造函數或 setter 方法用於注入 Item Bean,容器將使用反射機制將 Item 注入到 Store 中。

我們也可以使用 XML 配置來實現這一點。

這種方法可能看起來更簡單、更清晰,但我們不建議使用它,因為它存在一些缺點,例如:

  • 該方法使用反射注入依賴,比基於構造函數或 setter 方法的注入更昂貴。
  • 使用這種方法很容易添加多個依賴。如果我們使用基於構造函數注入,具有多個參數會讓我們認為該類執行了不止一件事情,這可能會違反單一職責原則。

有關 @Autowired 註解的更多信息,請參閲“在 Spring 中進行 Wiring”文章。

8. 自動注入依賴項

注入允許 Spring 容器通過檢查已定義的豆(bean)來自動解決協作豆之間的依賴關係。

以下是使用 XML 配置自動注入豆的四種模式:

  • 默認值 – 這意味着不使用任何自動注入,並且必須顯式指定依賴項。
  • ByName 自動注入基於屬性名稱進行,因此 Spring 將查找與需要設置的屬性名稱相同的豆。
  • ByType 類似於 ByName 自動注入,僅基於屬性類型。這意味着 Spring 將查找具有與屬性類型相同的豆進行設置。如果存在多個具有相同類型的豆,則框架會拋出異常。
  • Constructor 自動注入基於構造函數參數進行,這意味着 Spring 將查找具有與構造函數參數相同類型的豆。

例如,讓我們使用類型將上述 item 豆自動注入到 store 豆中:

@Bean(autowire = Autowire.BY_TYPE)
public class Store {
    private Item item;
    public setItem(Item item) {
        this.item = item;
    }
}

請注意,autowrie 屬性在 Spring 5.1 版本及更高版本中已棄用。

我們還可以使用 @Autowired 註解通過類型進行自動注入:

public class Store {
    
    @Autowired
    private Item item;
}

如果存在同類型的多個 Bean,我們可以使用 @Qualifier 註解通過名稱引用 Bean:

public class Store {
    
    @Autowired
    @Qualifier("item1")
    private Item item;
}

現在讓我們通過 XML 配置來通過類型自動注入 Bean:

<bean id="store" class="org.baeldung.store.Store" autowire="byType"> </bean>

接下來,我們通過 XML 將名為 item 的 Bean 注入到 store Bean 的 item 屬性中,通過名稱:

<bean id="item" class="org.baeldung.store.ItemImpl1" />

<bean id="store" class="org.baeldung.store.Store" autowire="byName">
</bean>

我們還可以通過構造函數參數或設置器來明確定義依賴項,從而覆蓋自動注入。

9. 延遲初始化 Bean

默認情況下,容器會在初始化時創建並配置所有單例 Bean。為了避免這種情況,可以使用 <em >lazy-init</em> 屬性,並將值設置為 <em >true</em>,在 Bean 的配置上:

<bean id="item1" class="org.baeldung.store.ItemImpl1" lazy-init="true" />

因此,item1 Bean 只會在首次請求時進行初始化,而不會在應用程序啓動時進行初始化。 這種方法的優勢在於初始化時間更快,但代價是,直到 Bean 被請求後,我們才能夠發現任何配置錯誤,這可能在應用程序已經運行數小時甚至數天之後才發生。

10. 結論

在本文中,我們介紹了控制反轉和依賴注入的概念,並在 Spring 框架中進行了示例説明。

您可以在 Martin Fowler 的文章中瞭解更多關於這些概念的信息:

此外,我們可以在 Spring 框架參考文檔 中學習 Spring 中 IoC 和 DI 的實現。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.