Spring之核心容器(IoC,DI,基本操作)詳解

Spring之核心容器IoC/DI/基本操作詳解

    • 一、核心概念:IoC與DI的本質
      • 1.1 IoC(Inversion of Control,控制反轉)
        • 傳統開發模式(無IoC)
        • IoC模式(Spring容器管理)
      • 1.2 DI(Dependency Injection,依賴注入)
        • DI的三種實現方式
      • 1.3 IoC容器的核心作用
    • 二、Spring容器的核心接口與實現類
      • 2.1 核心接口關系
      • 2.2 常用容器實現類
    • 三、Bean的定義與依賴注入(DI)實戰
      • 3.1 環境準備
      • 3.2 基于XML的Bean定義與注入
        • 3.2.1 定義Bean(XML配置)
        • 3.2.2 目標類(UserDao、UserService)
        • 3.2.3 啟動容器并使用Bean
      • 3.3 基于注解的Bean定義與注入(推薦)
        • 3.3.1 核心注解
        • 3.3.2 注解配置實戰
        • 3.3.3 啟動容器(基于注解配置)
      • 3.4 三種依賴注入方式對比
        • 3.4.1 構造器注入(推薦)
        • 3.4.2 Setter注入
        • 3.4.3 字段注入(簡潔但不推薦)
    • 四、Spring容器的基本操作
      • 4.1 容器的創建與關閉
        • 創建容器
        • 關閉容器
      • 4.2 獲取Bean的三種方式
      • 4.3 Bean的作用域(Scope)
      • 4.4 Bean的生命周期
        • 生命周期示例
    • 五、常見問題與避坑指南
      • 5.1 Bean的命名沖突
      • 5.2 循環依賴問題
      • 5.3 單實例Bean的線程安全問題

Spring框架的核心是IoC容器,它通過控制反轉(IoC)和依賴注入(DI)實現對象的管理與依賴解耦,是Spring所有功能的基礎。

一、核心概念:IoC與DI的本質

1.1 IoC(Inversion of Control,控制反轉)

IoC是一種設計思想,核心是將對象的創建權由開發者轉移給容器,實現“誰用誰創建”到“容器創建后注入”的轉變。

傳統開發模式(無IoC)
// 傳統方式:開發者手動創建對象
public class UserService {// 依賴UserDao,手動創建private UserDao userDao = new UserDaoImpl();public void addUser() {userDao.insert(); // 調用依賴對象的方法}
}

問題

  • 依賴硬編碼(new UserDaoImpl()),若更換實現類(如UserDaoMybatisImpl),需修改UserService源碼;
  • 對象創建與業務邏輯耦合,難以測試和擴展。
IoC模式(Spring容器管理)
// IoC方式:容器創建對象,開發者僅聲明依賴
public class UserService {// 依賴UserDao,由容器注入(無需手動new)@Autowiredprivate UserDao userDao;public void addUser() {userDao.insert();}
}

核心變化

  • 對象創建權轉移:UserDao的實例由Spring容器創建,而非UserService手動創建;
  • 依賴解耦:UserService僅依賴UserDao接口,不依賴具體實現,更換實現類無需修改源碼。

1.2 DI(Dependency Injection,依賴注入)

DI是IoC的具體實現方式,指容器在創建對象時,自動將依賴的對象注入到當前對象中。簡單說:IoC是思想,DI是手段。

DI的三種實現方式
  1. 構造器注入:通過構造方法傳入依賴對象;
  2. Setter注入:通過Setter方法設置依賴對象;
  3. 字段注入:通過注解直接標記字段(如@Autowired)。

后續會通過代碼示例詳細講解這三種方式。

1.3 IoC容器的核心作用

Spring的IoC容器(如ApplicationContext)本質是一個“對象工廠”,核心功能:

  1. 對象管理:創建、存儲、銷毀Bean(Spring對對象的稱呼);
  2. 依賴注入:自動將依賴的Bean注入到目標對象;
  3. 生命周期管理:控制Bean的初始化、銷毀等生命周期節點;
  4. 配置解析:讀取XML、注解等配置,解析Bean的定義。

二、Spring容器的核心接口與實現類

Spring提供了兩套核心容器接口:BeanFactoryApplicationContext,后者是前者的增強版,實際開發中優先使用ApplicationContext

2.1 核心接口關系

BeanFactory(基礎容器)└── ApplicationContext(高級容器,繼承BeanFactory)├── ClassPathXmlApplicationContext(XML配置,類路徑加載)├── FileSystemXmlApplicationContext(XML配置,文件系統加載)├── AnnotationConfigApplicationContext(注解配置)└── WebApplicationContext(Web環境專用)

2.2 常用容器實現類

容器實現類特點適用場景
ClassPathXmlApplicationContext從類路徑加載XML配置文件非Web項目,配置文件在src/main/resources
AnnotationConfigApplicationContext基于注解配置(如@Configuration注解驅動開發,無XML配置

三、Bean的定義與依賴注入(DI)實戰

3.1 環境準備

創建Maven項目,添加Spring核心依賴:

<dependencies><!-- Spring核心容器 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.20</version></dependency>
</dependencies>

3.2 基于XML的Bean定義與注入

3.2.1 定義Bean(XML配置)

創建src/main/resources/spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定義UserDao的Bean(id:唯一標識,class:全類名) --><bean id="userDao" class="com.example.dao.UserDaoImpl"/><!-- 定義UserService的Bean,并注入UserDao --><bean id="userService" class="com.example.service.UserService"><!-- Setter注入:通過setUserDao方法注入userDao --><property name="userDao" ref="userDao"/></bean>
</beans>
3.2.2 目標類(UserDao、UserService)
// UserDao接口
public interface UserDao {void insert();
}// UserDao實現類
public class UserDaoImpl implements UserDao {@Overridepublic void insert() {System.out.println("UserDaoImpl:插入用戶");}
}// UserService(需要注入UserDao)
public class UserService {private UserDao userDao;// Setter方法(用于Setter注入,方法名需對應XML中的property name)public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void addUser() {userDao.insert(); // 調用注入的UserDao}
}
3.2.3 啟動容器并使用Bean
public class Main {public static void main(String[] args) {// 1. 加載Spring配置文件,創建容器(ApplicationContext是IoC容器的核心接口)ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 2. 從容器中獲取UserService(參數為XML中定義的id)UserService userService = context.getBean("userService", UserService.class);// 3. 調用方法(依賴的UserDao已被容器注入)userService.addUser(); // 輸出:UserDaoImpl:插入用戶}
}

3.3 基于注解的Bean定義與注入(推薦)

注解配置比XML更簡潔,是現代Spring開發的主流方式。

3.3.1 核心注解
注解作用
@Component標記類為Bean(通用注解)
@Repository標記DAO層Bean(@Component的特例)
@Service標記Service層Bean(@Component的特例)
@Controller標記Controller層Bean(Web環境)
@Autowired自動注入依賴(默認按類型匹配)
@Configuration標記配置類(替代XML配置文件)
@ComponentScan掃描指定包下的注解Bean
3.3.2 注解配置實戰
// 1. 配置類(替代XML,掃描com.example包下的注解Bean)
@Configuration
@ComponentScan("com.example")
public class SpringConfig {// 無需手動定義Bean,通過@Component等注解自動掃描
}// 2. UserDaoImpl(用@Repository標記為Bean)
@Repository // 等價于<bean id="userDaoImpl" class="..."/>
public class UserDaoImpl implements UserDao {@Overridepublic void insert() {System.out.println("UserDaoImpl:插入用戶");}
}// 3. UserService(用@Service標記,并通過@Autowired注入UserDao)
@Service // 等價于<bean id="userService" class="..."/>
public class UserService {// 字段注入:直接在字段上標記@Autowired(無需Setter或構造器)@Autowiredprivate UserDao userDao;public void addUser() {userDao.insert();}
}
3.3.3 啟動容器(基于注解配置)
public class Main {public static void main(String[] args) {// 加載注解配置類,創建容器ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);// 獲取UserService(Bean id默認是類名首字母小寫:userService)UserService userService = context.getBean("userService", UserService.class);userService.addUser(); // 輸出:UserDaoImpl:插入用戶}
}

3.4 三種依賴注入方式對比

3.4.1 構造器注入(推薦)

通過構造方法注入依賴,確保對象創建時依賴已初始化:

@Service
public class UserService {private final UserDao userDao;// 構造器注入(@Autowired可省略,Spring 4.3+支持單構造器自動注入)@Autowiredpublic UserService(UserDao userDao) {this.userDao = userDao;}
}

優勢

  • 依賴不可變(final修飾),避免后續被修改;
  • 強制初始化依賴,防止null異常。
3.4.2 Setter注入

通過Setter方法注入,靈活性高(可在對象創建后修改依賴):

@Service
public class UserService {private UserDao userDao;@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}
}

優勢:適合可選依賴(可設置默認值)。

3.4.3 字段注入(簡潔但不推薦)

直接在字段上注入,代碼簡潔但存在缺陷:

@Service
public class UserService {@Autowiredprivate UserDao userDao; // 字段注入
}

缺陷

  • 無法注入final字段(構造器注入可以);
  • 依賴隱藏在字段中,不通過構造器或方法暴露,可讀性差;
  • 不利于單元測試(難以手動注入模擬對象)。

四、Spring容器的基本操作

4.1 容器的創建與關閉

創建容器
// 1. 基于XML(類路徑)
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 2. 基于XML(文件系統路徑)
ApplicationContext context = new FileSystemXmlApplicationContext("D:/spring.xml");// 3. 基于注解配置類
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
關閉容器

ApplicationContext無直接關閉方法,需通過ConfigurableApplicationContext

ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 關閉容器(觸發Bean的銷毀方法)
context.close();

4.2 獲取Bean的三種方式

// 1. 通過id獲取(返回Object,需強轉)
UserService userService1 = (UserService) context.getBean("userService");// 2. 通過id+類型獲取(推薦,無需強轉)
UserService userService2 = context.getBean("userService", UserService.class);// 3. 通過類型獲取(適合單實例Bean,存在多個同類型Bean時報錯)
UserService userService3 = context.getBean(UserService.class);

4.3 Bean的作用域(Scope)

Spring默認創建的Bean是單實例(singleton),可通過@Scope指定作用域:

@Service
@Scope("prototype") // 多實例:每次獲取Bean時創建新對象
public class UserService { ... }

常用作用域:

作用域說明適用場景
singleton單實例(默認),容器啟動時創建無狀態Bean(如Service、Dao)
prototype多實例,每次獲取時創建有狀態Bean(如Model、View)
request每個HTTP請求創建一個實例(Web環境)Web應用請求相關Bean
session每個會話創建一個實例(Web環境)Web應用會話相關Bean

4.4 Bean的生命周期

Spring容器管理Bean的完整生命周期:

  1. 實例化:創建Bean對象(調用構造方法);
  2. 屬性注入:注入依賴的Bean;
  3. 初始化:執行初始化方法(如@PostConstruct);
  4. 使用:Bean可被容器獲取并使用;
  5. 銷毀:容器關閉時執行銷毀方法(如@PreDestroy)。
生命周期示例
@Service
public class UserService {// 1. 實例化(構造方法)public UserService() {System.out.println("UserService:構造方法(實例化)");}// 2. 屬性注入(@Autowired)@Autowiredprivate UserDao userDao;// 3. 初始化方法(@PostConstruct標記)@PostConstructpublic void init() {System.out.println("UserService:初始化");}// 5. 銷毀方法(@PreDestroy標記)@PreDestroypublic void destroy() {System.out.println("UserService:銷毀");}
}

執行結果

UserService:構造方法(實例化)
UserService:初始化  // 容器啟動時執行
// 使用Bean...
UserService:銷毀    // 容器關閉時執行

五、常見問題與避坑指南

5.1 Bean的命名沖突

當容器中存在多個同類型Bean時,注入會報錯NoUniqueBeanDefinitionException

// 兩個UserDao實現類
@Repository
public class UserDaoImpl1 implements UserDao { ... }@Repository
public class UserDaoImpl2 implements UserDao { ... }// 注入時沖突
@Service
public class UserService {@Autowired // 報錯:存在兩個UserDao Beanprivate UserDao userDao;
}

解決方案

  1. @Qualifier指定Bean的id:
@Autowired
@Qualifier("userDaoImpl1") // 指定注入id為userDaoImpl1的Bean
private UserDao userDao;
  1. @Primary標記優先注入的Bean:
@Repository
@Primary // 優先注入
public class UserDaoImpl1 implements UserDao { ... }

5.2 循環依賴問題

兩個Bean互相依賴(A依賴B,B依賴A)會導致循環依賴:

@Service
public class AService {@Autowiredprivate BService bService;
}@Service
public class BService {@Autowiredprivate AService aService;
}

解決方案

  1. @Lazy延遲注入(打破即時依賴):
@Service
public class AService {@Autowired@Lazy // 延遲注入BServiceprivate BService bService;
}
  1. 改用Setter注入(構造器注入無法解決循環依賴)。

5.3 單實例Bean的線程安全問題

單實例Bean(默認)在多線程環境下,若存在共享狀態(如成員變量),會有線程安全問題:

@Service
public class UserService {// 共享狀態(多線程訪問會沖突)private int count = 0;public void increment() {count++; // 線程不安全操作}
}

解決方案

  1. 避免共享狀態(推薦):單實例Bean設計為無狀態(不定義成員變量);
  2. 改用prototype作用域(不推薦,性能差);
  3. 使用線程安全容器(如ThreadLocal)。

總結:Spring核心容器通過IoC和DI實現了對象的“按需創建”和“自動注入”:

  1. 依賴解耦:對象之間僅依賴接口,不依賴具體實現,降低耦合度;
  2. 簡化開發:開發者無需關注對象創建和依賴管理,專注業務邏輯;
  3. 可擴展性:通過配置或注解輕松更換Bean實現,無需修改業務代碼;
  4. 生命周期管理:容器統一管理Bean的創建、初始化、銷毀,便于資源控制。
    掌握Spring容器的核心是理解“容器是對象的管理者”:它創建對象、注入依賴、控制生命周期,是整個Spring生態的基礎。后續學習Spring的AOP、事務等功能,都需要以容器為基礎。

若這篇內容幫到你,動動手指支持下!關注不迷路,干貨持續輸出!
ヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノ

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91403.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91403.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91403.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【論文閱讀】基于注意力機制的冥想腦電分類識別研究(2025)

基于注意力機制的冥想腦電分類識別研究&#x1f4a1; Meta DataTitle基于注意力機制的冥想腦電分類識別研究Authors周梓涵Pub. date2025&#x1f4dc; Research Background & Objective背景&#xff1a; 現代生活壓力導致心理問題日益突出&#xff0c;冥想作為一種有效的心…

GitHub 上 Star 數量前 8 的開源 Web 應用項目

原文鏈接&#xff1a;https://www.nocobase.com/cn/blog/github-open-source-web-applications。 近期&#xff0c;我們發布了多篇「Top GitHub Star 開源項目推薦」系列文章&#xff0c;受到了大量點贊與收藏&#xff0c;很多開發者留言表示希望能看到更多不同領域的開源工具推…

FATFS文件系統原理及其移植詳解

一、FATFS簡介 FATFS 是一個完全免費開源的 FAT/exFAT 文件系統模塊&#xff0c;專門為小型的嵌入式系統而設計。它完全用標準 C 語言&#xff08;ANSI C C89&#xff09;編寫&#xff0c;所以具有良好的硬件平臺獨立性&#xff0c;只需做簡單的修改就可以移植到 8051、PIC、A…

KubeRay 和 Ray

KubeRay 和 Ray 不是替代關系&#xff0c;而是互補的協作關系。兩者在分布式計算生態中扮演不同角色&#xff0c;共同構成完整的云原生 AI 解決方案。以下是具體分析&#xff1a;&#x1f527; 1. 核心定位差異Ray 是分布式計算引擎&#xff0c;提供底層 API&#xff08;如 ray…

破解輪胎倉儲高密度與柔性管理難題

輪胎作為特殊的大件異形工業品&#xff0c;其倉儲管理長期面臨多重挑戰&#xff1a;規格型號繁雜導致SKU數量龐大&#xff0c;重型載重對貨架承重提出極高要求&#xff0c;橡膠材質對防壓變形、避光防老化等存儲環境存在嚴苛標準。傳統平置堆垛或普通貨架方案不僅空間利用率不足…

EVA series系列(上)

目錄 一、EVA 1、概述 2、方法 二、EVA-02 1、概述 2、架構 三、EVA-CLIP 1、概述 2、方法 四、EMU 1、概述 2、架構 3、訓練細節 4、評估 一、EVA 1、概述 為探尋大規模表征學習任務的MIM預訓練任務在ViT基礎上擴展到1B參數量規模&#xff0c;結合10M級別&am…

ABP VNext + EF Core 二級緩存:提升查詢性能

ABP VNext EF Core 二級緩存&#xff1a;提升查詢性能 &#x1f680; &#x1f4da; 目錄ABP VNext EF Core 二級緩存&#xff1a;提升查詢性能 &#x1f680;引言 &#x1f680;一、環境與依賴 &#x1f6e0;?二、集成步驟 ??2.1 安裝 NuGet 包2.2 注冊緩存服務與攔截器2…

3.1k star!推薦一款開源基于AI實現的瀏覽器自動化插件工具 !

大家好&#xff01;今天&#xff0c;我要給大家介紹一款超實用的開源工具——Chrome MCP Server&#xff01;這款工具不僅能大幅提升我們的工作效率&#xff0c;還能讓AI助手&#xff08;如Claude&#xff09;直接操控瀏覽器&#xff0c;實現自動化操作、內容分析等強大功能。 …

關于 OpenAI 的反思

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

Python爬蟲庫性能與選型對比

Python常用爬蟲庫的優勢對比。這是一個非常實用的問題&#xff0c;很多Python開發者都會面臨選擇合適爬蟲工具的困惑。我根據網絡很多搜索結果&#xff0c;整理出這些信息&#xff0c;為用戶提供一個全面且清晰的對比分析。以下是Python中常用爬蟲庫的核心優勢對比及選型建議&a…

NAT作業

拓撲圖 實驗要求 1.按照圖示配置IP地址&#xff0c;公網地址100.1.1.1/24..較網“說過?,使“掩入到互聯網&#xff0c;私服究的不到公的&#xff0c;使陽接入無三。.私網A通過NAPT&#xff0c;使R1接入到互聯網&#xff0c;私網B通過EASY,IP&#xff0c;使R3接入到互聯網實驗思…

JAVA進階--JVM

一.JVM的概述java語言有跨平臺特點, 寫一次java程序,可以在不同的平臺上運行.(JVM虛擬機的作用)前提條件: 在不同的平臺上安裝不同的虛擬機(虛擬機就是一個翻譯).java--->.class--->不同的虛擬機--->機器碼1.jvm作用:負責將字節碼翻譯為機器碼, 管理運行時內存2.jvm的…

基于Alpine構建MySQL鏡像

文章目錄基于Alpine構建MySQL鏡像一、基礎鏡像選擇與初始化1. 基礎鏡像選型2. 系統初始化二、核心配置構建1. 目錄與權限配置2. 配置文件優化三、安全增強配置1. 密碼策略強化2. 非root運行四、數據持久化與啟動配置1. 數據卷聲明2. 入口腳本優化五、完整Dockerfile示例六、關鍵…

Alamofire 網絡請求全流解析,通俗易懂

Alamofire 網絡請求全流程解析&#xff1a;從發起請求到處理響應 一、請求發起階段&#xff1a;準備你的"快遞" 1. 你告訴Alamofire要發什么"快遞" // 就像告訴快遞員&#xff1a;"我要寄一個包裹給https://api.example.com" AF.request("h…

鏈路聚合技術

鏈路聚合技術 鏈路聚合概述及應用場景 概述 鏈路聚合是把多條物理鏈路聚合在一起&#xff0c;形成一條邏輯鏈路。應用在交換機、路由器、服務器間鏈路&#xff0c;注意了&#xff0c;主機上面不能用鏈路聚合技術分為三層鏈路聚合和二層鏈路聚合鏈路聚合的作用 增加鏈路帶寬提供…

SpringCloud之Zuul

SpringCloud之Zuul 推薦參考&#xff1a;https://www.springcloud.cc/spring-cloud-dalston.html#_router_and_filter_zuul 1. 什么是Zuul Spring Cloud Zuul 是 Netflix 提供的微服務網關核心組件&#xff0c;作為統一的 API 入口&#xff0c;承擔請求路由、過濾、安全控制等…

低精度定時器 (timer_list) 和 高精度定時器 (hrtimer)

Linux 內核提供了兩種主要類型的定時器&#xff0c;以滿足不同的時間精度需求&#xff1a;低精度定時器 (timer_list) 和 高精度定時器 (hrtimer)。它們各有特點和適用場景。下面&#xff0c;我將分別提供它們在內核代碼中的簡化使用示例。1. 低精度定時器 (timer_list) 示例ti…

虛擬機VMware的使用方法

虛擬機VMware的使用方法VMware是全球領先的虛擬化技術提供商&#xff0c;其產品&#xff08;如VMware Workstation Pro&#xff09;允許用戶在單一物理機上運行多個操作系統&#xff08;OS&#xff09;&#xff0c;實現資源高效利用、隔離測試和靈活部署。本文將詳細介紹VMware…

冰島人(map)

#include<bits/stdc.h> using namespace std; struct people { string fat; int sex; }; map<string,people>mp; int pan(string s,string m) { string s1; int i0; while(s!“”) { int y0; s1m; while(s1!“”) { if(s1s&&(i<4||y<4)) return 0; s…

MS Azure Eventhub 發送 AD log 到cribl

1: 首先說一下,Cribl 提供了很多第三方的接口: 先看一下cribl 提供的接口界面: 注意到,上面提供的link 地址是 xxxxx:9093, 不鼠標放到撒謊給你嗎的? 上面,就可以看到了。所以要開的port 一定要把9093 開了,關于全部開的port: What ports do I need to open on the f…