Spring Boot的單例模式實現(xiàn)代碼
Spring Boot 里的單例不是 GoF 意義上的全 JVM 唯一對象 + 私有構(gòu)造器。
而是由 Spring 容器管理的每個 ApplicationContext 僅一個實例。
其本質(zhì)是 IOC 容器用緩存表保存已創(chuàng)建的 Bean,并在 getBean() 時復用。
默認就是單例
- 任何
@Component/@Service/@Repository/@Controller,或@Bean,默認 scope=singleton。 - 單例是容器級別:多個
ApplicationContext(測試、子上下文)各有一份。
@Component // 等價于 @Scope("singleton")
public class UserService {}容器如何保證只有一個
核心在 DefaultSingletonBeanFactory/DefaultSingletonBeanRegistry 的三級緩存與同步控制:
- 一級緩存
singletonObjects:已完全創(chuàng)建好的單例。 - 二級緩存
earlySingletonObjects:為解決循環(huán)依賴而“提前曝光”的半成品對象。 - 三級緩存
singletonFactories:可生成早期引用的工廠(通常是 AOP 代理工廠)。
創(chuàng)建流程(簡化):
getBean(name)→ 若在singletonObjects里,直接返回。- 不在 → 標記“正在創(chuàng)建”,調(diào)用
createBean()完成實例化、依賴注入、BeanPostProcessor等。 - 初始化完成后放入
singletonObjects,清理二/三級緩存,返回單例。 - 全過程對同名 bean 的創(chuàng)建有同步鎖,避免并發(fā)重復創(chuàng)建,并通過早期引用解決 A→B→A 的構(gòu)造/注入級循環(huán)依賴。
何時創(chuàng)建
- 默認容器刷新時預創(chuàng)建(
preInstantiateSingletons())。 - 標注
@Lazy或全局開啟spring.main.lazy-initialization=true時,延遲到首次getBean()才創(chuàng)建,但仍是單例。
線程安全與可見性
- 創(chuàng)建階段受容器同步保護,發(fā)布到
singletonObjects前已完成依賴注入與后置處理,具備安全發(fā)布語義。 - 但單例是多線程共享:業(yè)務字段若是可變狀態(tài),需自行保證線程安全(不可變、同步、并發(fā)集合等)。
@Bean 與 @Configuration 的細節(jié)
@Configuration類會被 CGLIB 增強,其@Bean方法默認proxyBeanMethods=true。- 在同一配置類內(nèi)部互相調(diào)用
@Bean方法時,調(diào)用會被攔截并從容器取同一個單例,不是 new 新對象。
- 在同一配置類內(nèi)部互相調(diào)用
- 若顯式
proxyBeanMethods=false(Spring Boot 為提速常用),同類內(nèi)直接方法調(diào)用將變成普通方法調(diào)用,可能得到新對象。 - 只要是通過容器
getBean()拿的,仍是單例。
經(jīng)驗:配置類間若互調(diào) @Bean 方法,保持默認 proxyBeanMethods=true 或避免直接互調(diào)。
常見誤區(qū)
- Spring 單例 = JVM 唯一 ?:是每個容器一個。
- 你起了兩個
ApplicationContext就有兩份。
- 你起了兩個
- 用 static/單例寫法再交給 Spring 管理 ?:多此一舉且易與容器生命周期沖突。
- 循環(huán)依賴全能解 ?:僅能解單例構(gòu)造器以外的注入型循環(huán)依賴,構(gòu)造器循環(huán)、原型 scope、帶代理的某些場景仍會失敗。
最小驗證
@SpringBootTest
class DemoTest {
@Autowired UserService a;
@Autowired ApplicationContext ctx;
@Test void sameInstance() {
UserService b = ctx.getBean(UserService.class);
assertSame(a, b); // 同一容器內(nèi)是同一個實例
}
}
小結(jié)
Spring Boot 的單例= 默認 scope + BeanFactory 單例緩存 + 同步創(chuàng)建 +(必要時)早期曝光。
把唯一性交給容器,比手寫 GoF 單例更易測、更可控,也能與 AOP、生命周期、條件化裝配等機制無縫協(xié)作。
到此這篇關于Spring Boot的單例模式實現(xiàn)代碼的文章就介紹到這了,更多相關Spring Boot單例模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
重試框架Guava-Retry和spring-Retry的使用示例
spring-retry 和 guava-retry 工具都是線程安全的重試,能夠支持并發(fā)業(yè)務場景的重試邏輯正確性,本文主要介紹了重試框架Guava-Retry和spring-Retry的使用示例,感興趣的可以一下2023-09-09
Java實戰(zhàn)之在線寄查快遞系統(tǒng)的實現(xiàn)
這篇文章主要介紹了如何利用Java制作一個在線寄查快遞系統(tǒng),文中采用的技術有java、SpringBoot、FreeMarker、Mysql,需要的可以參考一下2022-02-02
鴻蒙中使用WebView顯示W(wǎng)eb內(nèi)容的步驟
在鴻蒙操作系統(tǒng)中,Webview是一個強大的組件,它允許開發(fā)者將網(wǎng)頁內(nèi)容嵌入到應用程序中,實現(xiàn)與原生應用的無縫集成,這篇文章主要介紹了鴻蒙中使用WebView顯示W(wǎng)eb內(nèi)容的相關資料,需要的朋友可以參考下2025-09-09
java解析dbf之通過javadbf包生成和讀取dbf文件
這篇文章主要介紹了java通過javadbf讀取和生成DBF文件的方法,大家參考使用吧2014-01-01
Spring Mybatis Mapper模糊查詢的幾種方法
在Spring結(jié)合Mybatis進行開發(fā)時,實現(xiàn)模糊查詢是一個常見需求,在Mybatis中,LIKE查詢可以通過多種方式實現(xiàn),本文給大家介紹了Spring Mybatis Mapper模糊查詢的幾種方法,需要的朋友可以參考下2024-03-03
SpringBoot實現(xiàn)接口數(shù)據(jù)的加解密功能
這篇文章主要介紹了SpringBoot實現(xiàn)接口數(shù)據(jù)的加解密功能,對接口的加密解密操作主要有兩種實現(xiàn)方式,文中給大家詳細介紹,需要的朋友可以參考下2019-10-10

