面試官:Spring Bean 的生命週期都不會,你走吧下一位
面試官:看你簡歷上寫了不少Spring相關的項目經驗,那我們來聊聊Spring的核心概念吧。你能詳細説説Spring Bean的生命週期嗎?
我:Bean的生命週期?這個...我知道有創建、初始化、銷燬這些階段,但具體的細節和順序我有點記不清了...
解析答案
Spring Bean 生命週期的完整旅程
Spring Bean 的生命週期就像是一個人的一生,從出生(實例化)到成長(初始化),再到工作(使用),最後到退休(銷燬)。讓我們一起來探索這個精彩的過程:
第一階段:Bean 的誕生 - 實例化過程
1. BeanDefinition 的加載 Spring 首先讀取配置信息(XML、註解或Java配置),解析成 BeanDefinition 對象。這就像是給 Bean 辦理"出生證明"。
2. 構造方法的推斷 如果有多個構造方法,Spring 會智能地選擇最合適的那個:
- 優先選擇帶有
@Autowired註解的構造方法 - 如果沒有註解,選擇無參構造方法
- 如果只有一個有參構造方法,直接使用它
3. 對象的實例化 通過反射機制創建 Bean 的實例,這相當於"新生兒"的誕生。
第二階段:Bean 的成長 - 初始化過程
4. 屬性注入(依賴注入) Spring 自動為加了 @Autowired、@Value 等註解的屬性賦值:
@Component
public class UserService {
@Autowired
private UserRepository userRepository; // Spring 會自動注入
@Value("${app.name}")
private String appName; // 配置屬性注入
}
5. Aware 接口回調 如果 Bean 實現了特定的 Aware 接口,Spring 會回調相應方法:
BeanNameAware:感知自己的 Bean 名稱BeanFactoryAware:感知所在的 Bean 工廠ApplicationContextAware:感知應用上下文
6. BeanPostProcessor 的前置處理 這是 Spring 提供的重要擴展點,可以在初始化前進行自定義處理:
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化前處理: " + beanName);
return bean;
}
}
7. 初始化方法調用 Spring 會按順序調用初始化方法:
- 首先調用
@PostConstruct註解的方法 - 然後調用
InitializingBean接口的afterPropertiesSet()方法 - 最後調用 XML 中配置的
init-method方法
8. BeanPostProcessor 的後置處理 這是 AOP 代理創建的關鍵時機:
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 這裏可能會創建 AOP 代理對象
System.out.println("初始化後處理: " + beanName);
return bean;
}
}
第三階段:Bean 的輝煌 - 使用階段
9. 加入單例池 如果是單例 Bean,Spring 會將其放入單例池中,供後續使用。
10. 正式服役 此時 Bean 已經完全準備好,可以被應用程序正常使用了。
第四階段:Bean 的謝幕 - 銷燬過程
11. 容器關閉時的清理 當 Spring 容器關閉時,會按順序調用銷燬方法:
- 首先調用
@PreDestroy註解的方法 - 然後調用
DisposableBean接口的destroy()方法 - 最後調用 XML 中配置的
destroy-method方法
Bean 作用域:不同的"人生軌跡"
不同的 Bean 作用域決定了它們不同的"人生軌跡":
Singleton(單例)- 一生只愛一人
@Component
@Scope("singleton")
public class SingletonBean {
// 整個應用中只有一個實例
}
- Spring 默認的作用域
- 整個應用生命週期內共享同一個實例
- 適合無狀態的 Bean,如 Service、Repository
Prototype(原型)- 每次都是新的開始
@Component
@Scope("prototype")
public class PrototypeBean {
// 每次獲取都是新的實例
}
- 每次
getBean()都返回新的實例 - 適合有狀態的 Bean
- Spring 不管理完整的生命週期,只負責創建
Request(請求)- 一次請求一次人生
@Component
@Scope("request")
public class RequestBean {
// 每個 HTTP 請求都有新的實例
}
- 每個 HTTP 請求創建新的實例
- 請求結束時自動銷燬
- 適合存儲請求相關數據
Session(會話)- 一次會話一段旅程
@Component
@Scope("session")
public class SessionBean {
// 每個用户會話都有新的實例
}
- 每個用户會話創建新的實例
- 會話結束時自動銷燬
- 適合存儲用户會話數據
實際開發中的最佳實踐
1. 選擇合適的初始化時機
@Component
public class CacheService {
// 輕量級初始化放在構造方法
public CacheService() {
// 初始化基本數據結構
}
// 重量級初始化放在 @PostConstruct
@PostConstruct
public void init() {
// 加載緩存數據,建立網絡連接等
}
}
2. 正確處理異常
@Component
public class CriticalService {
@PostConstruct
public void init() {
try {
// 關鍵初始化邏輯
} catch (Exception e) {
// 記錄日誌並拋出異常,讓容器知道初始化失敗
throw new RuntimeException("初始化失敗", e);
}
}
}
3. 避免循環依賴
@Service
public class ServiceA {
// 構造方法注入可以避免某些循環依賴問題
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
歡迎交流
通過本文的詳細講解,相信你已經對 Spring Bean 的生命週期有了全面深入的理解。掌握這些知識不僅有助於面試,更能幫助你在實際開發中更好地使用 Spring 框架。以下幾個問題可以幫助你進一步思考:
- 在微服務架構中,Bean 的生命週期管理有什麼特殊的考慮?
- 如何利用 BeanPostProcessor 實現自定義的業務邏輯?
- 在雲原生環境下,Bean 的銷燬過程需要注意哪些問題?
歡迎在評論區分享你的見解和實踐經驗!
項目
項目適用人羣:做課設、畢設的小夥伴、只學習了後端(或者前端),但想要自己做項目寫在簡歷上,這三個項目可以作為拓展點。
項目有多線程、事務管理、Redis 緩存、買票問題、線程池、大模型調用等可以寫的點,而且體驗會發現接口響應速度是很快的,功能也比較實用,想要參與開源項目的 Commiter 也可以提出。
智能 AI 旅遊推薦平台:https://github.com/luoye6/vue3_tourism_frontend
智能 AI 校園二手交易平台:https://github.com/luoye6/vue3_trade_frontend
GPT 智能圖書館:https://github.com/luoye6/Vue_BookManageSystem
歡迎關注上方公眾號!感謝支持!一起進步,共勉!