Spring Bean初始化及@PostConstruc執(zhí)行順序示例詳解
1. Bean初始化執(zhí)行順序
構(gòu)造函數(shù) - 首先調(diào)用Bean的構(gòu)造函數(shù)
依賴注入 - 完成所有依賴項(xiàng)的注入(@Autowired等)
@PostConstruct方法 - 執(zhí)行帶有
@PostConstruct注解的方法InitializingBean的afterPropertiesSet() - 如果Bean實(shí)現(xiàn)了
InitializingBean接口自定義init方法 - 執(zhí)行通過
@Bean(initMethod="...")或XML配置中init-method指定的方法
@Component
public class ExampleBean implements InitializingBean {
public ExampleBean() {
System.out.println("1. 構(gòu)造函數(shù)");
}
@Autowired
public void setDependency(SomeDependency dep) {
System.out.println("2. 依賴注入");
}
@PostConstruct
public void postConstruct() {
System.out.println("3. @PostConstruct方法");
}
@Override
public void afterPropertiesSet() {
System.out.println("4. InitializingBean的afterPropertiesSet()");
}
@Bean(initMethod = "customInit")
public void customInit() {
System.out.println("5. 自定義init方法");
}
}2. 成員變量初始化順序
在Java中,類的成員變量(非靜態(tài)成員變量)的初始化時(shí)機(jī)取決于其定義方式(直接賦值、構(gòu)造器賦值、初始化塊等)以及是否涉及依賴注入(如Spring框架)。
2.1 普通Java類(非Spring環(huán)境)
成員變量的初始化順序和時(shí)機(jī)如下:
(1) 默認(rèn)初始化(即初始分配內(nèi)存)
時(shí)機(jī):對(duì)象實(shí)例化時(shí)(
new關(guān)鍵字調(diào)用構(gòu)造器之前)。規(guī)則:所有成員變量先被賦予默認(rèn)值(
int→0,boolean→false,引用類型→null等)。
(2) 顯式初始化
時(shí)機(jī):緊隨默認(rèn)初始化之后,按代碼中的聲明順序執(zhí)行。
方式:
直接賦值:
public class MyClass {
private int a = 10; // 顯式賦值
private String s = "hello"; // 顯式賦值
}- 初始化塊(代碼塊):
public class MyClass {
private int x;
{
x = 20; // 初始化塊賦值
}
}(3) 構(gòu)造器初始化
時(shí)機(jī):在顯式初始化之后,構(gòu)造器最后執(zhí)行。
特點(diǎn):構(gòu)造器中的賦值會(huì)覆蓋之前的默認(rèn)值或顯式賦值。
public class MyClass {
private int value;
public MyClass() {
this.value = 30; // 構(gòu)造器賦值
}
}(4)完整順序
默認(rèn)值 → 顯式賦值/初始化塊 → 構(gòu)造器賦值
2.2 Spring管理的Bean(依賴注入場(chǎng)景)
在Spring中,成員變量的初始化分為依賴注入和普通成員變量初始化兩部分:
(1) 普通成員變量
初始化規(guī)則與普通Java類一致(默認(rèn)值 → 顯式賦值 → 構(gòu)造器)。
示例:
@Component
public class MyBean {
private int count = 100; // 顯式賦值(Spring無(wú)關(guān))
public MyBean() {
this.count = 200; // 構(gòu)造器覆蓋
}
}(2) 依賴注入的成員變量
時(shí)機(jī):在Bean實(shí)例化后,由Spring容器通過反射或Setter方法注入。
字段注入(
@Autowired):
在構(gòu)造器和顯式賦值之后,通過反射直接注入。@Component public class MyBean { @Autowired private Dependency dependency; // Spring在對(duì)象構(gòu)造后注入 }構(gòu)造器注入:
在實(shí)例化時(shí)通過構(gòu)造器參數(shù)注入(等同于普通Java的構(gòu)造器賦值)。@Component public class MyBean { private final Dependency dependency; public MyBean(Dependency dependency) { this.dependency = dependency; // 構(gòu)造時(shí)注入 } }
(3) 生命周期回調(diào)的影響
@PostConstruct方法會(huì)在依賴注入完成后執(zhí)行,此時(shí)所有成員變量(包括注入的依賴)已就緒:@Component public class MyBean { @Autowired private Dependency dependency; @PostConstruct public void init() { System.out.println(dependency); // 依賴已注入 } }
(4)關(guān)鍵區(qū)別總結(jié)
| 場(chǎng)景 | 成員變量初始化時(shí)機(jī) |
|---|---|
| 普通Java類 | 默認(rèn)值 → 顯式賦值/初始化塊 → 構(gòu)造器賦值 |
| Spring Bean(字段注入) | 默認(rèn)值 → 顯式賦值 → 構(gòu)造器 → 依賴注入 → @PostConstruct |
| Spring Bean(構(gòu)造器注入) | 與普通Java類相同,依賴通過構(gòu)造器參數(shù)傳入 |
2.3 final成員變量
必須在聲明時(shí)、初始化塊或構(gòu)造器中賦值,否則編譯錯(cuò)誤。
public class MyClass {
private final int x = 10; // 聲明時(shí)賦值
private final int y;
{ y = 20; } // 初始化塊賦值
private final int z;
public MyClass() {
z = 30; // 構(gòu)造器賦值
}
}2.4 常見面試問題
Q1: 為什么構(gòu)造器里的賦值能覆蓋顯式賦值?
因?yàn)闃?gòu)造器是最后執(zhí)行的,可以修改之前的值。
Q2: 以下代碼的輸出是什么?
public class Test {
private int x = 10;
{ x = 20; }
public Test() {
System.out.println(x);
x = 30;
}
public static void main(String[] args) {
new Test(); // 輸出?
}
}
--
答案:20(顯式賦值和初始化塊先執(zhí)行,構(gòu)造器最后執(zhí)行)。到此這篇關(guān)于Spring Bean初始化及@PostConstruc執(zhí)行順序的文章就介紹到這了,更多相關(guān)Spring Bean初始化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中的char占幾個(gè)字節(jié)實(shí)例分析
這篇文章主要介紹了java中的char占幾個(gè)字節(jié)實(shí)例分析的相關(guān)資料,需要的朋友可以參考下2017-04-04
spring-Kafka中的@KafkaListener深入源碼解讀
本文主要通過深入了解源碼,梳理從spring啟動(dòng)到真正監(jiān)聽kafka消息的這套流程,從spring啟動(dòng)開始處理@KafkaListener,本文結(jié)合實(shí)例流程圖給大家講解的非常詳細(xì),需要的朋友參考下2023-02-02
使用Spring?Retry實(shí)現(xiàn)業(yè)務(wù)異常重試
在系統(tǒng)中經(jīng)常遇到業(yè)務(wù)重試的邏輯,比如三方接口調(diào)用,timeout重試三遍,異常處理重試的兜底邏輯等,本文給大家介紹一下如何使用Spring?Retry優(yōu)雅的實(shí)現(xiàn)業(yè)務(wù)異常重試,需要的朋友可以參考下2024-01-01
詳解SpringBoot中實(shí)現(xiàn)依賴注入功能
這篇文章主要介紹了詳解SpringBoot中實(shí)現(xiàn)依賴注入功能,SpringBoot的實(shí)現(xiàn)方式基本都是通過注解實(shí)現(xiàn)的。有興趣的可以了解一下。2017-04-04
怎樣通過分析GC日志來(lái)定位Java進(jìn)程的內(nèi)存問題
這篇文章主要介紹了怎樣通過分析GC日志來(lái)定位Java進(jìn)程的內(nèi)存問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-07-07
Java的RocketMq水平擴(kuò)展及負(fù)載均衡詳解
這篇文章主要介紹了Java的RocketMq水平擴(kuò)展及負(fù)載均衡詳解,RocketMQ是一個(gè)分布式具有高度可擴(kuò)展性的消息中間件,本文旨在探索在broker端,生產(chǎn)端,以及消費(fèi)端是如何做到橫向擴(kuò)展以及負(fù)載均衡的,需要的朋友可以參考下2024-01-01
Java8 中使用Stream 讓List 轉(zhuǎn) Map使用問題小結(jié)
這篇文章主要介紹了Java8 中使用Stream 讓List 轉(zhuǎn) Map使用總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-06-06

