BeanDefinition 與 Bean 生命周期(面試高頻考點)

Bean 是 Spring 應用的核心組件,而 BeanDefinition 作為 Bean 的 “元數據描述”,貫穿了 Bean 從定義到銷毀的全生命周期。理解 BeanDefinition 的加載注冊機制,以及 Bean 的完整生命周期,是掌握 Spring 容器管理邏輯的關鍵,也是面試中的高頻深挖點。本文結合源碼與面試場景,解析核心流程與實戰要點。

一、BeanDefinition:Bean 的 “藍圖” 與加載注冊機制

面試常問:Spring 是如何識別并管理 Bean 的?BeanDefinition 在其中扮演什么角色?

1. BeanDefinition 的核心作用

BeanDefinition 是 Spring 對 Bean 的 “抽象描述”,包含了創建 Bean 所需的全部信息:

  • 類信息:Bean 的全限定類名(getBeanClassName());
  • 作用域:單例(singleton)或原型(prototype,默認單例);
  • 屬性值:Bean 的屬性及依賴(如@Autowired注入的對象);
  • 初始化與銷毀方法:如@PostConstruct標注的方法、init-method配置;
  • 懶加載標識:是否延遲初始化(@Lazy注解對應isLazyInit())。

Spring 容器通過 BeanDefinition 的信息 “照圖施工”,創建并管理 Bean 實例。可以說,BeanDefinition 是 Bean 的 “藍圖”,容器的所有操作都基于此藍圖展開。

2. BeanDefinition 的加載與注冊流程(源碼解析)

BeanDefinition 的加載注冊是容器初始化的核心環節,以注解配置(@Component、@Bean)為例,流程如下:

(1)資源定位與掃描
  • 觸發點:@ComponentScan注解指定掃描路徑,由ConfigurationClassPostProcessor(BeanFactoryPostProcessor 的實現類)執行掃描。
  • 核心邏輯:ClassPathBeanDefinitionScanner.scan()方法遍歷指定包路徑,通過ClassPathScanningCandidateComponentProvider篩選符合條件的類(如帶@Component、@Service等注解的類)。
(2)BeanDefinition 的生成

掃描到的類會被解析為 BeanDefinition 實例(默認GenericBeanDefinition):

// 簡化邏輯:為目標類創建BeanDefinitionGenericBeanDefinition bd = new GenericBeanDefinition();bd.setBeanClassName(clazz.getName()); // 設置類名bd.setScope(BeanDefinition.SCOPE_SINGLETON); // 設置作用域bd.setLazyInit(false); // 非懶加載
(3)注冊到容器

生成的 BeanDefinition 會被注冊到DefaultListableBeanFactory的beanDefinitionMap(一個ConcurrentHashMap)中:

// DefaultListableBeanFactory的注冊方法public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);}
  • 注冊后:容器通過beanName即可從beanDefinitionMap中獲取 BeanDefinition,為后續 Bean 的創建提供依據。

面試應答技巧:回答 “BeanDefinition 的作用” 時,可類比 “建筑圖紙”—— 設計師(開發者)繪制圖紙(定義 Bean),施工隊(Spring 容器)根據圖紙建造房屋(創建 Bean),圖紙的修改(BeanDefinition 的動態調整)會直接影響最終建筑(Bean 實例)。

二、Bean 的完整生命周期:從實例化到銷毀的全鏈路

面試高頻問:Bean 從創建到銷毀經歷哪些階段?哪些擴展點可以干預 Bean 的生命周期?

Bean 的生命周期可概括為 “實例化→屬性填充→初始化→使用→銷毀” 五個階段,每個階段都有對應的擴展點(如后置處理器)。以單例 Bean 為例,核心流程如下:

1. 實例化(Instantiation)

  • 作用:創建 Bean 的實例(內存分配),尚未進行屬性設置。
  • 實現:通過反射調用 Bean 的構造方法(BeanUtils.instantiateClass())。
  • 擴展點:InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(),可在實例化前返回代理對象,跳過默認實例化流程(如 AOP 代理創建)。

2. 屬性填充(Population)

  • 作用:為 Bean 的屬性賦值,包括依賴注入(如@Autowired、@Resource)。
  • 實現:AbstractAutowireCapableBeanFactory.populateBean(),通過AutowiredAnnotationBeanPostProcessor解析@Autowired注解,完成依賴注入。
  • 關鍵邏輯:從容器中查找依賴的 Bean,若依賴未創建則觸發其生命周期(遞歸依賴處理)。

3. 初始化(Initialization)

初始化是 Bean 生命周期中擴展點最豐富的階段,核心步驟:

(1)執行 Aware 接口回調

Spring 通過 Aware 接口向 Bean 暴露容器內部組件,常見接口:

  • BeanNameAware:注入當前 Bean 的名稱;
  • BeanFactoryAware:注入 BeanFactory;
  • ApplicationContextAware:注入 ApplicationContext。

回調邏輯在AbstractAutowireCapableBeanFactory.invokeAwareMethods()中實現:

private void invokeAwareMethods(String beanName, Object bean) {if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}// 其他Aware接口回調...
}
(2)執行 BeanPostProcessor 的前置處理

BeanPostProcessor.postProcessBeforeInitialization():在初始化方法執行前對 Bean 進行加工,例如ApplicationContextAwareProcessor會處理ApplicationContextAware接口的回調。

(3)執行自定義初始化方法
  • @PostConstruct 注解:由CommonAnnotationBeanPostProcessor解析并執行;
  • init-method 配置:XML 中init-method屬性指定的方法,或@Bean(initMethod = "...");
  • InitializingBean 接口:執行afterPropertiesSet()方法。

執行順序:@PostConstruct → InitializingBean.afterPropertiesSet() → init-method。

(4)執行 BeanPostProcessor 的后置處理

BeanPostProcessor.postProcessAfterInitialization():在初始化方法執行后對 Bean 進行加工,AOP 代理就是在此步驟創建的(AbstractAutoProxyCreator的核心邏輯)。

4. 使用階段

Bean 初始化完成后,存入容器的單例池(singletonObjects),供應用程序通過getBean()獲取使用。

5. 銷毀階段

  • 觸發時機:容器關閉時(如ApplicationContext.close())。
  • 執行邏輯
  1. @PreDestroy注解標注的方法;
  2. DisposableBean.destroy()方法;
  3. XML 中destroy-method屬性或@Bean(destroyMethod = "...")指定的方法。

3. 生命周期擴展點實戰(面試場景題)

場景:如何在 Bean 初始化后自動注冊到某個管理中心(如緩存管理器、服務注冊中心)?

解決方案:自定義BeanPostProcessor,在postProcessAfterInitialization()中實現注冊邏輯:

@Component
public class RegistryBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 對特定類型的Bean進行注冊if (bean instanceof Cacheable) {CacheManager.register((Cacheable) bean);}return bean;}
}

面試考點:BeanPostProcessor與BeanFactoryPostProcessor的區別?

  • BeanFactoryPostProcessor:作用于 BeanDefinition,可修改 Bean 的元數據(如修改屬性值);
  • BeanPostProcessor:作用于 Bean 實例,可修改 Bean 本身(如創建代理、添加屬性)。

三、面試高頻問題與應答框架

1. 問:Bean 的實例化與初始化有什么區別?

應答框架

“實例化和初始化是 Bean 生命周期的兩個不同階段。

  • 實例化(Instantiation)是‘創建對象’的過程,通過反射調用構造方法分配內存,此時 Bean 還未設置屬性,處于‘半成品’狀態;
  • 初始化(Initialization)是‘完善對象’的過程,在屬性填充之后,會執行 Aware 接口回調、@PostConstruct方法等,最終將 Bean 變為‘成品’。

簡單說,實例化是‘從無到有’創建對象,初始化是‘從有到優’完善對象。”

2. 問:Spring 如何解決循環依賴?(核心難點)

應答框架

“Spring 通過‘三級緩存’機制解決單例 Bean 的循環依賴(如 A 依賴 B,B 依賴 A):

  • 一級緩存(singletonObjects):存儲初始化完成的 Bean;
  • 二級緩存(earlySingletonObjects):存儲實例化完成但未初始化的 Bean;
  • 三級緩存(singletonFactories):存儲 Bean 的工廠對象,用于生成早期代理對象。

解決流程:

  1. A 實例化后,將其工廠對象放入三級緩存;
  1. A 需要注入 B,觸發 B 的實例化;
  1. B 實例化后需要注入 A,從三級緩存獲取 A 的早期對象(若有 AOP 則生成代理),放入二級緩存;
  1. B 初始化完成,放入一級緩存,A 注入 B 后繼續初始化,最終放入一級緩存。

注意:原型 Bean 的循環依賴無法解決,會拋出BeanCurrentlyInCreationException。”

3. 問:@Autowired注入發生在 Bean 生命周期的哪個階段?

應答框架

“@Autowired注入發生在屬性填充階段(populateBean()方法),在實例化之后、初始化之前。

具體來說,由AutowiredAnnotationBeanPostProcessor(InstantiationAwareBeanPostProcessor的實現類)的postProcessProperties()方法完成:

  1. 解析 Bean 中的@Autowired注解,找到依賴的 Bean;
  2. 從容器中獲取依賴的 Bean(若未創建則觸發其生命周期);
  3. 將依賴注入到當前 Bean 的屬性中。

因此,在@PostConstruct標注的初始化方法中,可以安全使用@Autowired注入的依賴。”

四、實戰總結

BeanDefinition 的加載注冊是 Spring 管理 Bean 的基礎,而 Bean 的生命周期則體現了容器對 Bean 的 “全生命周期管理”。掌握這些知識,不僅能應對面試中的深度提問,更能在實戰中通過擴展點(如BeanPostProcessor)定制 Bean 的行為,解決復雜業務場景問題。

下一篇將解析 Spring AOP 的底層實現,包括動態代理選擇邏輯、切面織入流程及@Transactional注解的原理,這也是面試中的重點難點。

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

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

相關文章

node.js 學習筆記2 進程/線程、fs

進程和線程 進程:進行中的程序。比如有一段程序,程序已經載入內存了,CPU正在執行這段程序,這時候就會產生一個進程。進程,也可以看做程序的一次執行過程。 在window中打開任務管理器,可以查看計算機中的所…

【線性代數】其他

上一節:【線性代數】線性方程組與矩陣——(3)線性方程組解的結構 總目錄:【線性代數】目錄 文章目錄11. 向量的內積、長度及正交性12. 方陣的特征值與特征向量13. 相似矩陣14. 對稱矩陣的對角化15. 二次型及其標準形11. 向量的內積…

Spring Cloud LoadBalancer 實現自定義負載均衡策略(基于服務元數據篩選)

💡 Spring Cloud LoadBalancer 實現自定義負載均衡策略(基于服務元數據篩選) 在微服務架構中,我們常常希望對服務實例進行更精細的路由控制,例如: 灰度發布:不同環境訪問不同版本操作系統差異&a…

Javaweb(1)html、css、js

注:圖來自黑馬 一、HTML(超文本標記語言) HTML 是網頁的 “骨架”,負責定義頁面的結構和內容,通過標簽(tag)描述文本、圖片、鏈接等元素。 1. 基礎結構 文檔聲明:<!DOCTYPE html>(告訴瀏覽器這是 HTML5 文檔)。 根標簽:<html> 包裹整個文檔,包含 &l…

MQTT:Dashboard數據集成(待補充)

目錄一、工作原理二、基本使用三、連接器基本使用一、工作原理 數據集成使用sink和source組件與外部數據系統對接。 sink&#xff1a;用于將消息發送到外部數據系統&#xff0c;例如MySQL、Kafka或Http服務等。source&#xff1a;用于從外部數據系統接收消息&#xff0c;例如…

VisionMoE本地部署的創新設計:從架構演進到高效實現

本地部署VisionMoE的時代需求 在人工智能技術飛速發展的今天&#xff0c;視覺語言模型(Vision-Language Models, VLMs)已成為多模態理解的核心工具。然而&#xff0c;傳統的大型視覺語言模型主要依賴云端GPU集群進行部署和推理&#xff0c;這不僅帶來了高昂的運營成本&#xf…

機試備考筆記 8/31

2025年8月8日 小結&#xff1a;省流&#xff0c;寫了倆道巨簡單的&#xff08;被卡好久的傳參指針和指針的引用的區別&#xff09;&#xff0c;一題遞歸&#xff08;意滿&#xff09;&#xff1b;這筆記還是0809寫的&#xff0c;嘖&#xff0c;今天可能不寫了&#xff0c;明天也…

java9學習筆記-part2

進程 API在 Java 9 之前&#xff0c;Process API 仍然缺乏對使用本地進程的基本支持&#xff0c;例如獲取進程的 PID 和所有者&#xff0c;進程的開始時間&#xff0c;進程使用了多少 CPU 時間&#xff0c;多少本地進程正在運行等。Java 9 向 Process API 添加了一個名為 Proce…

AI智能編程工具匯總

AI智能編程工具匯總 以下是一份關于主流大模型開發工具的綜合介紹&#xff0c;涵蓋 Gemini CLI、Qwen-Code、Kimi K2 等關鍵工具的功能特性、安裝方式與使用建議。 &#x1f31f; Gemini CLI 開發者&#xff1a;Google DeepMind 簡介&#xff1a;命令行工具&#xff0c;用于調…

算法_python_牛客華為機試筆記_01

刷題是必須的&#xff0c;通過刷題以及別人對題目的解析&#xff0c;可以快速理解&#xff0c;提高效率。 00_題庫與參考視頻 華為機試_在線編程_牛客網 HJ3 明明的隨機數_嗶哩嗶哩_bilibili 這套華為機試是華為筆試面試機考在線練習&#xff0c;共138道題&#xff0c;目前…

Java基礎-完成局域網內溝通軟件的開發

目錄 案例要求&#xff1a; 實現思路&#xff1a; itheima-chat-server包 src com.itheima Constant類&#xff1a; Server類: ServerReaderThread類: itheima-chat-system包 src com.itheima.ui ChatEntryFrame類&#xff1a; ClientChatFrame類: ClientReaderTh…

windows內核研究(內存管理-線性地址的管理)

內存管理線性地址的管理 進程空間的地址劃分分區x86 32位Windows空指針賦值區0x00000000 - 0x0000FFFF用戶模式區0x00010000 - 0x7FFEFFFF64KB禁入區0x7FFF0000 - 0x7FFFFFFF內核0x80000000 - 0xFFFFFFFF線性地址有4GB&#xff0c;但是并不是所有的地方都能訪問&#xff08;這里…

【問題解決】使用patch-package修改node-models中的源碼

文章目錄一、應用場景二、patch-package 和 postinstallpatch-packagepostinstall三、操作步驟1、使用yarn安裝patch-package和postinstall-postinstall2、修改package.json3、修改node-model中源碼、保存。4、找到修改文件對應的包名5、使用git將新增的patches文件同步到倉庫6…

當配置項只支持傳入數字,即無法指定單位為rem,需要rem轉px

您好&#xff01;針對您 Vue 3 Element Plus 的技術棧&#xff0c;要優雅且符合大廠規范地解決這個問題&#xff0c;最佳實踐是創建一個響應式的 Composition API (組合式函數)。 這個方法完全遵循 Vue 3 的設計哲學&#xff0c;具有高內聚、低耦合、可復用、類型安全&#xf…

谷歌搜索 sg_ss 逆向分析

聲明: 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01;部分python代碼sg_ss cp.call(get_sg_…

一個“加鎖無效“的詭異現象

加鎖了還出問題&#xff1f;從"點擊過快"到"狀態可控"&#xff1a;多線程共享變量的并發陷阱與實戰對策詳情如下&#xff1a;在服務端開發中&#xff0c;多線程并發處理客戶端請求是提升系統吞吐量的常見手段。最近有位開發者朋友遇到了一個令人費解的問題…

液體泄漏識別誤報率↓76%:陌訊多模態融合算法實戰解析

原創聲明本文為原創技術解析&#xff0c;核心技術參數與架構設計引用自《陌訊技術白皮書》&#xff0c;禁止未經授權的轉載與篡改。一、行業痛點&#xff1a;液體泄漏識別的現實挑戰在化工生產、食品加工、倉儲物流等場景中&#xff0c;液體泄漏的實時監測是保障安全生產的關鍵…

Y9000P跑開源模型(未完成)

環境信息 1、Y9000筆記本 2、1T空白硬盤 3、ubunut24.04桌面版 一、環境初始化 第一部分&#xff1a;系統初始化 1、安裝基礎軟件 apt-get update apt-get -y install openssh-server openssh-client apt-utils freeipmi ipmitool sshpass ethtool zip unzip nano less git ne…

ARM體系結構

ARM體系結構 編程原理 從源代碼到CPU執行過程 #mermaid-svg-M4xemCxDjIQVNNnW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:14px;fill:#333;}#mermaid-svg-M4xemCxDjIQVNNnW .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mer…

基于SpringBoot的高校社團管理系統的設計與實現(代碼+LW文檔+遠程運行)

&#x1f4af;博主&#xff1a;?全網擁有50W粉絲、博客專家、全棧領域優質創作者、平臺優質Java創作者、專注于Java技術領域和畢業項目實戰?&#x1f4af; &#x1f497;開發技術&#xff1a;SpringBoot、Vue、SSM、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、…