Spring——Spring相關類原理與實戰

摘要

本文深入探討了 Spring 框架中 InitializingBean 接口的原理與實戰應用,該接口是 Spring 提供的一個生命周期接口,用于在 Bean 屬性注入完成后執行初始化邏輯。文章詳細介紹了接口定義、作用、典型使用場景,并與其他相關概念如 @PostConstruct 和 DisposableBean 進行了對比。

1. InitializingBean原理與實戰

InitializingBean 是 Spring 提供的一個生命周期接口,其核心作用是讓 Bean 在所有屬性注入完成后執行一些初始化邏輯。

1.1. 🧩 接口定義

public interface InitializingBean {void afterPropertiesSet() throws Exception;
}

如果你的類實現了這個接口,Spring 會在完成依賴注入后自動調用 afterPropertiesSet() 方法。

1.2. ? InitializingBean作用

功能

說明

生命周期鉤子

在 Bean 初始化(注入屬性)后執行自定義邏輯

替代 @PostConstruct

是一種“接口驅動”的初始化方式

適用于框架開發

明確初始化點,便于統一管理

1.3. ? 典型使用場景(實戰)

1.3.1. 初始化資源、連接等

@Component
public class RedisClient implements InitializingBean {private JedisPool jedisPool;@Value("${redis.host}")private String host;@Value("${redis.port}")private int port;@Overridepublic void afterPropertiesSet() throws Exception {jedisPool = new JedisPool(host, port);System.out.println("Redis 連接池初始化完成");}public Jedis getConnection() {return jedisPool.getResource();}
}

📌 實戰說明:配置注入完成后,通過 afterPropertiesSet() 初始化 Redis 連接池。

1.3.2. 校驗依賴是否注入完整

@Component
public class SomeService implements InitializingBean {@Autowiredprivate SomeDependency dependency;@Overridepublic void afterPropertiesSet() {if (dependency == null) {throw new IllegalStateException("SomeDependency 沒有被注入!");}}
}

1.3.3. 在框架中設置靜態訪問點(如 XXL-Job 中)

@Override
public void afterPropertiesSet() throws Exception {adminConfig = this; // 設置靜態訪問入口xxlJobScheduler = new XxlJobScheduler();xxlJobScheduler.init(); // 初始化調度器
}

📌 實戰說明:用于啟動調度器、加載配置等初始化邏輯,適合中間件開發。

1.4. ? InitializingBean總結

  • InitializingBean 提供了一種 標準化的 Bean 初始化入口
  • 推薦用于:底層框架、組件、工具類初始化中,如連接池、調度器、配置校驗。
  • 對于業務邏輯,更推薦用 @PostConstruct 注解,簡潔易讀。

2. @PostConstruct注解原理與實戰

@PostConstruct 是 Java 提供的標準注解(來自 javax.annotationjakarta.annotation),在 Spring 中用于定義 Bean 的初始化方法,當 Bean 完成依賴注入之后自動執行。

2.1. ? @PostConstruct 是什么?

@PostConstruct
public void init() {// 初始化邏輯
}
  • 當 Spring 完成對 Bean 的創建與依賴注入后,會自動調用標注了 @PostConstruct 的方法。
  • 方法 只能有一個、無參數、返回值為 void

2.2. ? @PostConstruct與Spring生命周期的關系

Spring 創建一個 Bean 的完整流程如下:

構造函數 -> 依賴注入 -> @PostConstruct -> InitializingBean.afterPropertiesSet() -> Bean 初始化完成

因此,@PostConstruct 執行在 Bean 初始化的早期階段,非常適合做以下操作:

用途

說明

資源初始化

建立連接池、定時器、緩存等

參數檢查

校驗注入的配置是否符合要求

注冊操作

向注冊中心、事件總線等注冊自己

靜態賦值

注入靜態成員變量或構造輔助工具

2.3. ? 實戰使用示例

2.3.1. 緩存初始化

@Component
public class DictCache {private final DictService dictService;private Map<String, String> dictCache = new HashMap<>();public DictCache(DictService dictService) {this.dictService = dictService;}@PostConstructpublic void loadCache() {dictCache = dictService.loadAllDict();System.out.println("字典緩存加載完畢!");}public String get(String key) {return dictCache.get(key);}
}

2.3.2. 參數校驗或默認值設置

@Component
public class SmsSender {@Value("${sms.gateway.url}")private String gatewayUrl;@PostConstructpublic void validate() {if (gatewayUrl == null || gatewayUrl.isEmpty()) {throw new IllegalArgumentException("短信網關地址不能為空!");}}
}

2.3.3. 設置靜態工具類

@Component
public class SpringContextUtil {@Autowiredprivate ApplicationContext applicationContext;public static ApplicationContext context;@PostConstructpublic void init() {SpringContextUtil.context = this.applicationContext;}public static <T> T getBean(Class<T> clazz) {return context.getBean(clazz);}
}

2.4. ? @PostConstruct與InitializingBean 的對比

特性

@PostConstruct

InitializingBean

來源

Java 標準注解

Spring 接口

侵入性

低(無需實現接口)

高(必須實現接口)

可讀性

更清晰、簡潔

稍顯繁瑣

推薦

? 推薦

? 一般不推薦用于業務代碼

場景

普通業務初始化

框架、組件級初始化

3. @PostConstructInitializingBean區別?

緩存初始化 場景中,@PostConstructInitializingBean 都能完成初始化邏輯,但兩者有以下核心區別

  • @PostConstruct 是注解驅動的初始化方式,簡潔、解耦、推薦用于業務代碼
  • InitializingBean 是接口驅動的初始化方式,侵入性強,推薦用于框架/中間件級別代碼

3.1. ? 功能與使用上的對比

對比項

@PostConstruct

InitializingBean

本質

Java 標準注解(JSR-250)

Spring 生命周期接口

編碼方式

在方法上加注解

實現接口、重寫方法

方法名

任意(如 init()

固定:afterPropertiesSet()

方法個數

可以多個類中各定義一個

一個類只能有一個 afterPropertiesSet()

侵入性

? 低:無須繼承或實現接口

? 高:必須實現接口

可讀性

? 強:一看注解就知道是初始化

? 差:容易被忽視

可測試性

? 好(不會影響類結構)

? 接口實現影響結構

場景適用

業務代碼、工具類、緩存加載

框架設計、底層組件、可復用模塊

推薦程度

? 更推薦使用

?? 更適合框架代碼

3.2. ? 實際緩存初始化場景對比

3.2.1. 用@PostConstruct 初始化緩存

@Component
public class DictCache {@Autowiredprivate DictService dictService;private Map<String, String> cache = new HashMap<>();@PostConstructpublic void initCache() {cache = dictService.loadAllDict();}
}

? 優點

  • 簡潔明了
  • 不影響類結構
  • 易于測試和維護

3.2.2. 用InitializingBean 初始化緩存

@Component
public class DictCache implements InitializingBean {@Autowiredprivate DictService dictService;private Map<String, String> cache = new HashMap<>();@Overridepublic void afterPropertiesSet() throws Exception {cache = dictService.loadAllDict();}
}

?? 缺點

  • 需要實現接口,影響類設計
  • 方法名固定,不夠語義化
  • 可讀性差:不能一眼看出這是初始化方法

3.3. ? 實際開發推薦

場景

推薦方式

普通業務初始化(如緩存、參數)

? @PostConstruct

框架開發 / 高通用組件(如連接池、調度器)

? InitializingBean

初始化邏輯中涉及多個階段、多個順序

?都不推薦,建議用 @Bean(initMethod = "...") 或配置類

4. DisposableBean原理與實戰

DisposableBean 是 Spring 提供的一個生命周期接口,用于在 Bean 被銷毀時執行清理邏輯,常用于釋放資源、關閉連接、銷毀線程池等操作。DisposableBean 接口的 destroy() 方法會在 Spring 容器銷毀該 Bean 之前被調用,用于完成資源釋放或清理工作。

4.1. 🧩 接口定義

public interface DisposableBean {void destroy() throws Exception;
}

Spring 會在 容器關閉前 調用實現類的 destroy() 方法。

4.2. ? 釋放線程池

@Component
public class TaskManager implements DisposableBean {private ExecutorService threadPool = Executors.newFixedThreadPool(10);public void submit(Runnable task) {threadPool.submit(task);}@Overridepublic void destroy() throws Exception {System.out.println("正在關閉線程池...");threadPool.shutdown();}
}

📝 當 Spring 容器關閉時,destroy() 方法被自動調用,安全關閉線程池。

4.3. ? 關閉數據庫連接或 Redis 客戶端

java復制編輯
@Component
public class RedisClientWrapper implements DisposableBean {private JedisPool jedisPool = new JedisPool("localhost");public Jedis getClient() {return jedisPool.getResource();}@Overridepublic void destroy() throws Exception {System.out.println("關閉 Redis 連接池");jedisPool.close();}
}

4.4. ? 結合 InitializingBean

有時你會在一個類中同時使用初始化和銷毀邏輯:

@Component
public class MyService implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("初始化資源");}@Overridepublic void destroy() throws Exception {System.out.println("清理資源");}
}

4.5. 🛠? 替代方案:@PreDestroy

Spring 也支持使用 @PreDestroy 注解實現銷毀邏輯:

@PreDestroy
public void cleanup() {System.out.println("釋放資源 @PreDestroy");
}

4.5.1. 🔍 區別總結:

對比項

DisposableBean

@PreDestroy

來源

Spring 接口

Java 標準注解(JSR-250)

入侵性

高(實現接口)

低(注解)

推薦程度

? 較低

? 更推薦

方法限制

固定 destroy()

方法名可自定義

結構清晰

? 不利于多繼承

? 解耦、靈活

4.6. ? 總結

項目

說明

接口名

DisposableBean

方法

destroy()

調用時機

Bean 被銷毀前(如容器關閉)

常見用途

釋放線程池、關閉連接池、清理緩存等

推薦替代

使用 @PreDestroy更靈活、非侵入式

使用場景

資源管理類、任務調度類、連接池等生命周期敏感組件

如果你在業務中需要在 Spring 應用關閉時清理資源,推薦使用 @PreDestroy;如果你正在寫一個框架、組件,或者需要精確控制 Bean 生命周期,則可以使用 DisposableBean

5. SmartInitializingSingleton原理與實戰

SmartInitializingSingleton 是 Spring 框架中的一個 高級擴展接口,用于在 所有單例 Bean 完全初始化完成后 執行某段邏輯。它常用于 組件啟動邏輯的延遲執行,比如像 XXL-JOB 的執行器、緩存預熱、動態注冊。

5.1. SmartInitializingSingleton 是什么?

public interface SmartInitializingSingleton {void afterSingletonsInstantiated();
}
  • Spring 會在所有單例 Bean 實例化并依賴注入完成后(即 Spring 容器即將就緒時)調用這個接口的實現。
  • InitializingBean.afterPropertiesSet() 不同,它只調用一次,且是在所有單例準備完畢后調用。

5.2. 與InitializingBean區別?

項目

InitializingBean

SmartInitializingSingleton

調用時機

單個 Bean 初始化后

所有單例 Bean 初始化后

觸發方式

每個 Bean 單獨觸發

容器完成所有初始化后統一觸發

應用場景

Bean 自己初始化邏輯

全局依賴邏輯(如依賴其他 Bean 已完成)

替代方式

@PostConstruct

無注解替代方式(需接口或 SmartLifecycle

實現目標

Bean 自己初始化

等待所有 Bean 初始化再統一處理

5.3. 實戰場景和原理解析

5.3.1. 🌟 場景一:自動注冊任務(如 XXL-JOB)

@Component
public class XxlJobSpringExecutor extends XxlJobExecutor
implements SmartInitializingSingleton {@Overridepublic void afterSingletonsInstantiated() {initJobHandlerMethodRepository(applicationContext); // 掃描 @XxlJobsuper.start(); // 啟動注冊}
}

📌 原因:只有當所有 Bean 都加載完,才能掃描所有 Bean 中的方法。

5.3.2. 🌟 場景二:事件總線初始化(如 Guava EventBus)

@Component
public class EventBusRegister implements SmartInitializingSingleton {@Autowiredprivate ApplicationContext applicationContext;@Overridepublic void afterSingletonsInstantiated() {// 注冊所有 @EventListener 的 BeanapplicationContext.getBeansWithAnnotation(MyEventListener.class).forEach((name, bean) -> EventBusFactory.getBus().register(bean));}
}

5.3.3. 🌟 場景三:注冊中心上報(如 Dubbo、Nacos)

@Component
public class ServiceRegistry implements SmartInitializingSingleton {@Autowiredprivate ServiceMetaInfo localService;@Overridepublic void afterSingletonsInstantiated() {// 等所有 Bean 初始化完后統一注冊服務信息registryCenter.register(localService);}
}

5.4. 源碼調用順序

Spring 在 DefaultListableBeanFactory.preInstantiateSingletons() 方法中:

if (bean instanceof SmartInitializingSingleton) {((SmartInitializingSingleton) bean).afterSingletonsInstantiated();
}

該方法會在所有單例 Bean 初始化完成后,逐個調用實現了 SmartInitializingSingleton 的類的回調方法

5.5. ? 總結對比

項目

InitializingBean

SmartInitializingSingleton

接口方法

afterPropertiesSet()

afterSingletonsInstantiated()

調用時機

Bean 實例化 + 屬性注入后

所有單例實例化完成后

調用頻率

每個 Bean 調一次

所有單例后只調一次(全局)

適用場景

Bean 自己的初始化

需要獲取所有 Bean 后統一處理

替代注解

@PostConstruct

無明確替代,最末生命周期回調

5.6. ? 實戰建議

需求

建議實現

初始化當前 Bean 用

@PostConstructInitializingBean

需要等待所有單例準備好后統一處理(如注冊、掃描)

SmartInitializingSingleton

如果你在項目中需要:

  • 自動發現所有某類 Bean(如任務、監聽器、規則處理器)
  • 注冊第三方服務(Nacos、Dubbo、Job)
  • 需要確保 Spring 容器啟動完成再初始化邏輯

推薦用 SmartInitializingSingleton,這是一種更穩妥的方式。

博文參考

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

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

相關文章

Angular微前端架構:Module Federation + ngx-build-plus (Webpack)

以下是一個完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 實現了主應用&#xff08;Shell&#xff09;與子應用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;? 項目結構 angular-mf/ ├── shell-app/ # 主應用&…

ESP32 I2S音頻總線學習筆記(四): INMP441采集音頻并實時播放

簡介 前面兩期文章我們介紹了I2S的讀取和寫入&#xff0c;一個是通過INMP441麥克風模塊采集音頻&#xff0c;一個是通過PCM5102A模塊播放音頻&#xff0c;那如果我們將兩者結合起來&#xff0c;將麥克風采集到的音頻通過PCM5102A播放&#xff0c;是不是就可以做一個擴音器了呢…

馮諾依曼架構是什么?

馮諾依曼架構是什么&#xff1f; 馮諾依曼架構&#xff08;Von Neumann Architecture&#xff09;是現代計算機的基礎設計框架&#xff0c;由數學家約翰馮諾依曼&#xff08;John von Neumann&#xff09;及其團隊在1945年提出。其核心思想是通過統一存儲程序與數據&#xff0…

【持續更新】linux網絡編程試題

問題1 請簡要說明TCP/IP協議棧的四層結構&#xff0c;并分別舉出每一層出現的典型協議或應用。 答案 應用層&#xff1a;ping,telnet,dns 傳輸層&#xff1a;tcp,udp 網絡層&#xff1a;ip,icmp 數據鏈路層&#xff1a;arp,rarp 問題2 下列協議或應用分別屬于TCP/IP協議…

橢圓曲線密碼學(ECC)

一、ECC算法概述 橢圓曲線密碼學&#xff08;Elliptic Curve Cryptography&#xff09;是基于橢圓曲線數學理論的公鑰密碼系統&#xff0c;由Neal Koblitz和Victor Miller在1985年獨立提出。相比RSA&#xff0c;ECC在相同安全強度下密鑰更短&#xff08;256位ECC ≈ 3072位RSA…

【JVM】- 內存結構

引言 JVM&#xff1a;Java Virtual Machine 定義&#xff1a;Java虛擬機&#xff0c;Java二進制字節碼的運行環境好處&#xff1a; 一次編寫&#xff0c;到處運行自動內存管理&#xff0c;垃圾回收的功能數組下標越界檢查&#xff08;會拋異常&#xff0c;不會覆蓋到其他代碼…

React 基礎入門筆記

一、JSX語法規則 1. 定義虛擬DOM時&#xff0c;不要寫引號 2.標簽中混入JS表達式時要用 {} &#xff08;1&#xff09;.JS表達式與JS語句&#xff08;代碼&#xff09;的區別 &#xff08;2&#xff09;.使用案例 3.樣式的類名指定不要用class&#xff0c;要用className 4.內…

Linux鏈表操作全解析

Linux C語言鏈表深度解析與實戰技巧 一、鏈表基礎概念與內核鏈表優勢1.1 為什么使用鏈表&#xff1f;1.2 Linux 內核鏈表與用戶態鏈表的區別 二、內核鏈表結構與宏解析常用宏/函數 三、內核鏈表的優點四、用戶態鏈表示例五、雙向循環鏈表在內核中的實現優勢5.1 插入效率5.2 安全…

SQL進階之旅 Day 19:統計信息與優化器提示

【SQL進階之旅 Day 19】統計信息與優化器提示 文章簡述 在數據庫性能調優中&#xff0c;統計信息和優化器提示是兩個至關重要的工具。統計信息幫助數據庫優化器評估查詢成本并選擇最佳執行計劃&#xff0c;而優化器提示則允許開發人員對優化器的行為進行微調。本文深入探討了…

安寶特方案丨船舶智造AR+AI+作業標準化管理系統解決方案(維保)

船舶維保管理現狀&#xff1a;設備維保主要由維修人員負責&#xff0c;根據設備運行狀況和維護計劃進行定期保養和故障維修。維修人員憑借經驗判斷設備故障原因&#xff0c;制定維修方案。 一、痛點與需求 1 Arbigtec 人工經驗限制維修效率&#xff1a; 復雜設備故障的診斷和…

MFC內存泄露

1、泄露代碼示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 獲取 Ribbon Bar 指針// 創建自定義按鈕CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…

基于區塊鏈的供應鏈溯源系統:構建與實踐

前言 在當今全球化的經濟環境中&#xff0c;供應鏈的復雜性不斷增加&#xff0c;商品從原材料采購到最終交付給消費者的過程涉及多個環節和眾多參與者。如何確保供應鏈的透明度、可追溯性和安全性&#xff0c;成為企業和消費者關注的焦點。區塊鏈技術以其去中心化、不可篡改和透…

Web攻防-SQL注入數據格式參數類型JSONXML編碼加密符號閉合

知識點&#xff1a; 1、Web攻防-SQL注入-參數類型&參數格式 2、Web攻防-SQL注入-XML&JSON&BASE64等 3、Web攻防-SQL注入-數字字符搜索等符號繞過 案例說明&#xff1a; 在應用中&#xff0c;存在參數值為數字&#xff0c;字符時&#xff0c;符號的介入&#xff0c…

探秘鴻蒙 HarmonyOS NEXT:實戰用 CodeGenie 構建鴻蒙應用頁面

在開發鴻蒙應用時&#xff0c;你是否也曾為一個頁面的布局反復調整&#xff1f;是否還在為查 API、寫模板代碼而浪費大量時間&#xff1f;今天帶大家實戰體驗一下鴻蒙官方的 AI 編程助手——CodeGenie&#xff08;代碼精靈&#xff09; &#xff0c;如何從 0 到 1 快速構建一個…

DBAPI如何優雅的獲取單條數據

API如何優雅的獲取單條數據 案例一 對于查詢類API&#xff0c;查詢的是單條數據&#xff0c;比如根據主鍵ID查詢用戶信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默認返回的數據格式是多條的&#xff0c;如下&#xff1a; {&qu…

使用Whisper本地部署實現香港版粵語+英語混合語音轉文字方案

今天要一個非常好的朋友有個工作&#xff0c;就是要把醫院醫生診斷的說話記錄轉成文字&#xff0c;之前都是她本人一句一句的聽&#xff0c;然后記錄下來的&#xff0c;我想通過ai 來解決這個問題。 她的需求如下&#xff1a; 不能把數據傳到網上&#xff0c;隱私問題所以需要…

案例分享--汽車制動卡鉗DIC測量

制動系統是汽車的主要組成部分&#xff0c;是汽車的主要安全部件之一。隨著車輛性能的不斷提高&#xff0c;車速不斷提升&#xff0c;對車輛的制動系統也隨之提出了更高要求&#xff0c;因此了解車輛制動系統中每個部件的動態行為成為了制動系統優化的主要途徑&#xff0c;同時…

保姆級教程:在無網絡無顯卡的Windows電腦的vscode本地部署deepseek

文章目錄 1 前言2 部署流程2.1 準備工作2.2 Ollama2.2.1 使用有網絡的電腦下載Ollama2.2.2 安裝Ollama&#xff08;有網絡的電腦&#xff09;2.2.3 安裝Ollama&#xff08;無網絡的電腦&#xff09;2.2.4 安裝驗證2.2.5 修改大模型安裝位置2.2.6 下載Deepseek模型 2.3 將deepse…

【Redis技術進階之路】「原理分析系列開篇」分析客戶端和服務端網絡誦信交互實現(服務端執行命令請求的過程 - 初始化服務器)

服務端執行命令請求的過程 【專欄簡介】【技術大綱】【專欄目標】【目標人群】1. Redis愛好者與社區成員2. 后端開發和系統架構師3. 計算機專業的本科生及研究生 初始化服務器1. 初始化服務器狀態結構初始化RedisServer變量 2. 加載相關系統配置和用戶配置參數定制化配置參數案…

VB.net復制Ntag213卡寫入UID

本示例使用的發卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、讀取舊Ntag卡的UID和數據 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click輕松讀卡技術支持:網站:Dim i, j As IntegerDim cardidhex, …