深度解析Spring Bean生命周期:從字節碼到可用對象的奇幻旅程

🌱 深度解析Spring Bean生命周期:從字節碼到可用對象的奇幻旅程

你是否曾困惑:為什么@PostConstruct有時不執行?為什么循環依賴報錯如此難解?為什么AOP代理在某些場景失效? 本文將徹底拆解Spring Bean的16個關鍵生命周期階段,讓你不僅理解原理,更能解決實際開發中的痛點問題。

🔥 一、痛點直擊:Bean生命周期誤解引發的生產事故

@Service
public class PaymentService {@Autowiredprivate RiskService riskService; // 有時為null?@PostConstructpublic void init() {// 在哪些情況下不會執行?riskService.loadRules(); }public void process() {// 方法調用時NPE}
}

典型問題

  • 依賴注入時機錯誤導致NPE
  • 初始化邏輯未執行引發業務異常
  • 銷毀方法未調用造成資源泄漏

?? 二、全景流程圖:Bean的16個關鍵生命周期階段

graph TDA[Bean定義加載] --> B[BeanFactoryPostProcessor]B --> C[實例化]C --> D[屬性填充]D --> E[Aware接口回調]E --> F[BeanPostProcessor前置]F --> G[@PostConstruct]G --> H[InitializingBean]H --> I[init-method]I --> J[BeanPostProcessor后置]J --> K[AOP代理]K --> L[加入單例池]L --> M[運行期使用]M --> N[DisposableBean]N --> O[@PreDestroy]O --> P[destroy-method]

關鍵區分:實例化(內存分配) vs 初始化(業務準備)


🧪 三、深度解析:核心階段的技術內幕

階段1:Bean定義注冊(容器啟動時)
// 關鍵源碼:DefaultListableBeanFactory
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);
}// 實戰技巧:動態注冊Bean
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(OrderService.class);
階段2:BeanFactory后處理(修改Bean定義)
@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory factory) {BeanDefinition bd = factory.getBeanDefinition("dataSource");bd.getPropertyValues().add("maxWait", 3000); // 動態修改配置}
}
階段3:實例化 - 不是簡單的new!
  • 選擇構造器:優先用@Autowired構造器
  • 解決循環依賴:三級緩存曝光早期引用
  • 特殊Bean處理:FactoryBean的特殊創建邏輯
階段4:屬性注入 - 比你想的更復雜
// 注入過程偽代碼
for (PropertyValue pv : beanDefinition.getPropertyValues()) {Field field = reflection.findField(pv.getName());Object value = resolveDependency(pv.getValue()); // 可能遞歸創建依賴Beanfield.set(beanInstance, value);
}
階段5:Aware接口回調 - 獲取容器基礎設施
接口名注入資源典型應用場景
BeanNameAware當前Bean名稱動態代理生成BeanName
BeanFactoryAwareBeanFactory實例手動獲取其他Bean
ApplicationContextAwareApplicationContext實現ApplicationContextUtil
階段6:初始化三重奏(嚴格順序!)
  1. @PostConstruct(JSR-250標準)

    @Service
    public class CacheService {@PostConstruct // 最先執行public void loadCache() { /* 預熱緩存 */ }
    }
    
  2. InitializingBean(Spring原生接口)

    @Service
    public class PaymentService implements InitializingBean {@Override // 其次執行public void afterPropertiesSet() { /* 檢查支付網關 */ }
    }
    
  3. init-method(XML/注解配置)

    @Bean(initMethod = "init")
    public DataSource dataSource() { return new DruidDataSource(); }
    
階段7:BeanPostProcessor - AOP的誕生地
// 關鍵實現:AbstractAutoProxyCreator
public Object postProcessAfterInitialization(Object bean, String beanName) {if (isEligible(bean)) {// 創建代理對象(JDK/CGLib)return createProxy(bean);}return bean;
}

🚨 四、高頻問題診斷表

癥狀可能原因解決方案
@Autowired字段為nullBean未實例化檢查類是否被@Component掃描
@PostConstruct未執行循環依賴導致提前曝光@Lazy打破循環
AOP代理失效內部方法調用通過AopContext獲取代理對象
銷毀方法未調用原型Bean不被容器管理生命周期手動調用context.close()

🛠? 五、高級技巧:掌控生命周期的三種武器

1. 自定義BeanPostProcessor實現熱插拔
@Component
public class EncryptionProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String name) {if (bean instanceof SensitiveData) {return encrypt((SensitiveData)bean); // 數據自動加密}return bean;}
}
2. SmartInitializingSingleton - 所有單例就緒后的回調
@Component
public class SystemValidator implements SmartInitializingSingleton {@Overridepublic void afterSingletonsInstantiated() {// 當所有單例Bean初始化完成后執行checkSystemIntegrity();}
}
3. 優雅銷毀:@PreDestroy vs DisposableBeanAdapter
// 推薦方式:注解聲明
@Service
public class ResourceHolder {@PreDestroy // 容器關閉時自動調用public void release() {// 釋放文件句柄/網絡連接}
}// 外部JAR類銷毀適配
@Bean(destroyMethod = "shutdown")
public ThirdPartyService service() { /* ... */ }

🚀 六、生命周期可視化:Spring Boot Actuator實戰

# application.yml
management:endpoints:web:exposure:include: beansendpoint:beans:enabled: true

訪問 http://localhost:8080/actuator/beans 獲取:

{"beans": [{"bean": "paymentService","scope": "singleton","type": "com.example.PaymentService","dependencies": ["riskService"],"initializationTime": 42 // 初始化耗時(ms)}]
}

💡 七、設計思想升華:生命周期擴展的哲學

  1. 開閉原則典范

    • 不修改容器源碼即可擴展功能(BeanPostProcessor)
    • 標準接口 vs 自定義實現
  2. 控制反轉的深度實踐

    傳統編程Spring IOC
    主動創建對象聲明依賴關系
    控制對象生命周期響應容器事件
  3. 微服務下的新挑戰

    • Kubernetes生命周期鉤子:preStop -> @PreDestroy
    • 配置刷新:@RefreshScope重建Bean

終極面試題:Spring如何解決構造器循環依賴?
答案:三級緩存(singletonFactories)暴露早期引用,通過ObjectFactory延遲解決依賴。

掌握生命周期的核心價值
? 精準解決Bean初始化順序問題
? 深度定制Spring容器行為
? 設計高擴展性的企業級組件
? 面試中展現系統級理解能力

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

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

相關文章

MySQL 復合查詢和內外連接 -- 子查詢,多表查詢,自連接,合并查詢,表的內外連接

目錄 1. 子查詢 1.1 單行子查詢 1.2 多行子查詢 1.3 多列子查詢 1.4 在 from 子句中使用子查詢 2. 多表查詢 3. 自連接 4. 合并查詢 4.1 union 4.2 union all 5. 表的內連接 6. 表的外連接 下列先給出該博客中所用到的所有表的數據。 (1)部…

【STM32+LAN9252+HAL庫】EtherCAT從站搭建 保姆級教程

目錄 一、生成協議棧及XML文件 二、使用stm32CuboMX配置外設 三、協議棧移植 鑒于本人對EtherCAT的掌握程度十分有限,這篇文章僅作為我搭建基礎從站的過程記錄不做更多講解。本文內容主要為SPI模式的基礎搭建,更多深入的學習資料和細節,大家…

【LeetCode 熱題 100】239. 滑動窗口最大值——(解法二)滑動窗口+單調隊列

Problem: 239. 滑動窗口最大值 題目:給你一個整數數組 nums,有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 k 個數字。滑動窗口每次只向右移動一位。返回滑動窗口中的最大值 。 【LeetCode 熱題 100】239. 滑…

MySQL 8.0 連接 5.x 服務器認證問題

總的來說,答案是:可以,但是需要特別注意認證方式的兼容性問題。 MySQL 8.0 引入了新的默認認證插件 caching_sha2_password,而 MySQL 5.x(及更早版本)使用的是 mysql_native_password。當你用一個 8.0 的客…

Spring原理揭秘(一)

什么是spring? spring框架是一個輕量級的開源的JavaEE框架。 所謂輕量級則是:占用空間小,代碼侵入性低,代碼耦合度低,降低代碼復雜度,可以輕易適配多種框架。 隨著spring的不斷發展,它所占用…

Visual Studio Code自用搜索技巧整理

多文件跨行搜索 用途 在多個日志文件中搜索跨行日志 方法 1.用VS Code打開待搜索文件所在的目錄; 2.按快捷鍵(CtrlShiftF)打開全局搜索; 3.點擊搜索框右側的開啟正則表達式; 4.輸入正則表達式,例如&…

Axure PR 9 驗證碼登錄 案例

大家好,我是大明同學。 這期內容,我們來用Axure來制作一個短信驗證登錄頁面的小案例。 驗證碼登錄小案例 創建手機號輸入框所需的元件 1.打開一個新的 RP 文件并在畫布上打開 Page 1。 2.在元件庫中拖出一個矩形元件,選中矩形元件&#xf…

監聽器模式

1. 問題背景 假設我們有一個 銀行賬戶管理系統,該系統需要監控用戶賬戶余額的變動,并在發生變動時,自動執行一些相關的操作,比如發送 余額變動通知(如短信、郵件等)。為了實現這一功能,我們希望…

帕魯杯應急響應賽題:知攻善防實驗室

一、背景信息 在這個跳躍的數字舞臺上,數據安全成了政企單位穩航的重要壓艙石。某政企單位,作為一艘駛向未來 的巨輪,對數據的把控絲毫不敢松懈。眼下,我們即將啟航一場無與倫比的探險——“信息安全探索之 旅”。 這趟旅程的目的…

【硬核數學】2.2 深度學習的“微積分引擎”:自動微分與反向傳播《從零構建機器學習、深度學習到LLM的數學認知》

歡迎來到本系列的第七篇文章。在上一章,我們用張量武裝了我們的線性代數知識,學會了如何描述和操作神經網絡中的高維數據流。我們知道,一個神經網絡的“前向傳播”過程,就是輸入張量經過一系列復雜的張量運算(矩陣乘法…

DAY 45 Tensorboard使用介紹

浙大疏錦行https://blog.csdn.net/weixin_45655710知識點回顧: tensorboard的發展歷史和原理tensorboard的常見操作tensorboard在cifar上的實戰:MLP和CNN模型 作業:對resnet18在cifar10上采用微調策略下,用tensorboard監控訓練過程…

2023年全國碩士研究生招生考試英語(一)試題總結

文章目錄 題型與分值分布完形填空錯誤 1:考察連詞 or 前后內容之間的邏輯關系錯誤2:錯誤3:錯誤4:這個錯得最有價值,因為壓根沒讀懂錯誤5:學到的短語: 仔細閱讀排序/新題型翻譯小作文大作文 題型…

react-數據Mock實現——json-server

什么是mock? 在前后端分離的開發模式下,前端可以在沒有實際后端接口的支持下先進行接口數據的模擬,進行正常的業務功能開發 json-server實現數據Mock json-server是一個node的包,可以在不到30秒內獲得零編碼的完整Mock服務 實現…

使用POI導入解析excel文件

首先校驗 /*** 校驗導入文件* param file 上傳的文件* return 校驗結果,成功返回包含成功狀態的AjaxResult,失敗返回包含錯誤信息的AjaxResult*/private AjaxResult validateImportFile(MultipartFile file) {if (file.isEmpty()) {return AjaxResult.er…

從0開始學習計算機視覺--Day06--反向傳播算法

盡管解析梯度可以讓我們省去巨大的計算量,但如果函數比較復雜,對這個損失函數進行微分計算會變得很困難。我們通常會用反向傳播技術來遞歸地調用鏈式法則來計算向量每一個方向上的梯度。具體來說,我們將整個計算過程的輸入與輸入具體化&#…

企業流程知識:《學習觀察:通過價值流圖創造價值、消除浪費》讀書筆記

《學習觀察:通過價值流圖創造價值、消除浪費》讀書筆記 作者:邁克魯斯(Mike Rother),約翰舒克(John Shook) 出版時間:1999年 歷史地位:精益生產可視化工具的黃金標準&am…

Day02_C語言IO進程線程

01.思維導圖 02.將當前的時間寫入到time. txt的文件中,如果ctrlc退出之后,在再次執行支持斷點續寫 1.2022-04-26 19:10:20 2.2022-04-26 19:10:21 3.2022-04-26 19:10:22 //按下ctrlc停止,再次執行程序 4.2022-04-26 20:00:00 5.2022-04-26 2…

FFmpeg中TS與MP4格式的extradata差異詳解

在視頻處理中,extradata是存儲解碼器初始化參數的核心元數據,直接影響視頻能否正確解碼。本文深入解析TS和MP4格式中extradata的結構差異、存儲邏輯及FFmpeg處理方案。 📌 一、extradata的核心作用 extradata是解碼必需的參數集合&#xff0…

【CV數據集介紹-40】Cityscapes 數據集:助力自動駕駛的語義分割神器

🧑 博主簡介:曾任某智慧城市類企業算法總監,目前在美國市場的物流公司從事高級算法工程師一職,深耕人工智能領域,精通python數據挖掘、可視化、機器學習等,發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN…

SAP月結問題9-FAGLL03H與損益表中研發費用金額不一致(FAGLL03H Bug)

SAP月結問題9-FAGLL03H與損益表中研發費用金額不一致(S4 1709) 財務反饋,月結后核對數據時發現FAGLL03H導出的研發費用與損益表中的研發費用不一致,如下圖所示: 對比FAGLL03H與損益表對應的明細,發現FAGLL03H與損益表數據存在倍數…