技术博客
深入解析Spring单例Bean的源码实现与创建过程

深入解析Spring单例Bean的源码实现与创建过程

作者: 万维易源
2026-06-18
源码解析单例BeanSpring框架Bean创建内部机制

本文由 AI 阅读网络公开技术资讯生成,力求客观但可能存在信息偏差,具体技术细节及数据请以权威来源为准

摘要

本文以源码逐行解析为切入点,深入剖析Spring框架中单例Bean的完整创建过程,涵盖BeanDefinition加载、实例化、属性填充、初始化及注册至单例缓存(singletonObjects)等核心阶段。通过追踪AbstractBeanFactory与DefaultListableBeanFactory等关键类的执行路径,揭示Spring如何在首次getBean调用时完成线程安全的单例构建与三级缓存协同机制,展现其Bean管理的精巧内部机制。

关键词

源码解析,单例Bean,Spring框架,Bean创建,内部机制

一、Spring框架基础与Bean管理机制

1.1 Spring容器初始化过程:BeanFactory与ApplicationContext的区别与联系

在Spring框架浩繁的抽象体系中,容器初始化并非一蹴而就的仪式,而是一场精密编排的启程——它悄然决定了后续所有Bean命运的起点。BeanFactory是Spring最基础的IoC容器接口,提供最基本的依赖查找与生命周期管理能力;而ApplicationContext则在其之上构建,不仅继承全部BeanFactory功能,更扩展了事件发布、国际化、AOP集成等企业级特性。二者的关系,恰如静水与奔流:BeanFactory是深潜于底层的稳定内核,ApplicationContext则是浮出水面、呼吸万象的完整生态。当开发者调用new AnnotationConfigApplicationContext(...)时,真正的初始化才真正开始——它会触发refresh()方法,依次执行环境准备、BeanFactory创建、配置类解析、BeanDefinition注册等关键步骤。值得注意的是,此时尚未发生任何Bean实例化;一切尚处于“定义就绪、待命而未动”的临界状态。这种分层设计,既保障了轻量级使用的可能性,又为复杂场景预留了充分延展空间——这正是Spring历经多年演进仍屹立不倒的底层智慧。

1.2 BeanDefinition的注册与解析:Spring如何管理Bean的定义信息

BeanDefinition,是Spring眼中Bean的“数字基因图谱”——它不描述实例,却完整编码了该Bean将如何被创建:类名、作用域、是否懒加载、构造参数、属性值、初始化方法、后置处理器……一切皆在此中凝练成结构化的元数据。在ConfigurationClassPostProcessor的驱动下,Spring扫描@Configuration类、@Bean方法、@Component注解类,并将它们逐一解析为BeanDefinition对象,最终注册至DefaultListableBeanFactorybeanDefinitionMap中。这一过程看似静默,实则暗藏机锋:同一个Bean可能因不同配置路径被多次定义,Spring需依序合并、覆盖、校验;而@Lazy@Primary@Scope("singleton")等注解,则如刻刀般精细雕琢每一份定义的语义边界。正是这些尚未具象的“蓝图”,构成了后续所有实例化、依赖注入与生命周期回调的唯一依据——没有它,getBean()便无从知晓该造什么、怎么造、为谁而造。源码逐行解析至此,我们终于触碰到Spring灵魂深处那条不可逾越的铁律:定义先行,实例在后;蓝图未定,万物不生。

二、单例Bean的创建流程源码分析

2.1 单例Bean的创建入口:getBean方法源码解析

当开发者第一次调用getBean("xxx")时,Spring的静默世界骤然被一道指令点亮——这不是一次普通的对象索取,而是一场精密调度的起点。getBean作为单例Bean生命周期的正式入口,其背后是AbstractBeanFactory中层层嵌套的逻辑跃迁:从doGetBean开始,方法首先检查三级缓存(singletonObjectsearlySingletonObjectssingletonFactories)是否存在已就绪或正在创建中的实例;若未命中,则触发createBean流程。尤为关键的是,此处的线程安全并非依赖粗粒度锁,而是借由ConcurrentHashMap的无锁读+同步块写机制,配合beforeSingletonCreationafterSingletonCreation的原子标记,在高并发场景下悄然守护单例的唯一性。一行行追踪下去,我们看到的不只是方法调用栈的延伸,更是一个设计哲学的具象化:延迟加载不是妥协,而是对资源最审慎的敬意;首次访问的毫秒级开销,换来的是后续所有请求的零成本复用。 这一入口,既是技术实现的咽喉要道,亦是Spring“按需构建、全局共享”理念最锋利的落笔。

2.2 Bean实例化过程:从反射到对象实例的完整流程

实例化绝非简单的一句clazz.getDeclaredConstructor().newInstance()——在AbstractAutowireCapableBeanFactorycreateBeanInstance方法中,Spring以近乎考古般的耐心,逐一分辨构造器策略:优先尝试带参构造器自动装配,失败则回退至无参构造器+反射实例化;若存在@Lookup或工厂方法,则交由SimpleInstantiationStrategyCglibSubclassingInstantiationStrategy接管。此时,类加载、字节码校验、构造器可访问性检查、参数类型匹配……每一环都如齿轮咬合般严丝合缝。更值得凝视的是,实例化完成后的对象尚处于“裸体”状态:无属性填充、无Aware回调、无初始化方法执行——它只是Spring容器中一个被赋予了内存地址的、沉默的“初生体”。正是这种刻意为之的阶段性解耦,使得InstantiationAwareBeanPostProcessor得以在实例化后、初始化前插入定制逻辑,为AOP代理、字段注入等高级能力预留不可替代的钩子。源码逐行解析至此,我们恍然:所谓“创建”,从来不是一锤定音的终点,而是一次郑重其事的启程——对象在此刻诞生,却在后续每一步中,被反复定义、塑造、确认,直至成为那个被整个应用所信赖的单例Bean。

三、Bean的属性填充与初始化过程

3.1 属性填充阶段:Spring如何完成Bean的依赖注入

当“初生体”在内存中悄然落定,它仍是一具未被赋予关系的空壳——没有依赖,没有上下文,亦无处安放其存在意义。此时,populateBean方法如一位沉静而精准的织网者,在AbstractAutowireCapableBeanFactory中徐徐展开属性填充的全程。它不急于注入,而是先校验Bean是否可修改、是否被@Lookup@Autowired标记、是否存在InstantiationAwareBeanPostProcessor的前置干预;随后,依序执行@Autowired字段注入、@Value值解析、@Resource按名称装配,并在必要时触发resolveDependency进行类型匹配与循环依赖探测。尤为精妙的是,这一阶段与三级缓存机制深度咬合:若当前Bean被其他Bean依赖,而自身尚未初始化完毕,Spring便提前将“早期引用”(early reference)暴露至earlySingletonObjects,从而在不破坏单例语义的前提下,为循环依赖提供合法出口。一行行源码翻过,我们触摸到的不仅是反射设值与类型转换的代码逻辑,更是一种对“关系”的郑重承诺——依赖不是强加的枷锁,而是经由定义校验、类型协商、时机权衡后,主动缔结的生命契约;每一次setFieldValue的调用,都是Spring在混沌的引用图谱中,亲手点亮的一盏确认之灯。

3.2 初始化方法调用:afterPropertiesSet与自定义初始化方法

属性落定之后,对象仍未真正“活过来”——它尚需一次庄严的自我确认:唤醒Aware接口赋予的容器感知能力,执行用户声明的初始化逻辑,最终通过InitializingBean.afterPropertiesSet()@PostConstruct标注的方法完成人格赋形。此阶段由initializeBean统一调度,在AbstractAutowireCapableBeanFactory中层层推进:先调用invokeAwareMethods,将BeanFactoryAwareApplicationContextAware等接口注入对应上下文引用,使Bean从“被管理”跃升为“知管理”;继而遍历BeanPostProcessor.postProcessBeforeInitialization,为AOP代理、属性增强等预留钩子;再执行invokeInitMethods,严格区分InitializingBean回调与自定义init-method的调用顺序与异常处理策略;最后,再经postProcessAfterInitialization完成终态封装。整个过程如一场精密的加冕仪式——没有跳步,不容省略,每一步都受try-catch严密包裹,任一环节失败即终止流程并抛出BeanCreationException。源码逐行解析至此,我们终于彻悟:所谓“初始化”,并非技术流程的收尾,而是Spring对单例Bean所立下的根本誓约——它必须知情、必须就绪、必须可控;唯有如此,那个被注册进singletonObjects的引用,才不只是一个内存地址,而是一个真正可信赖、可追溯、可托付的运行时主体。

四、循环依赖处理机制

4.1 循环依赖的解决方案:Spring如何处理Bean之间的相互依赖

当两个单例Bean——A与B——彼此持有对方的引用,且均被声明为@Scope("singleton")时,一场静默的僵局便在内存中悄然形成:A的创建需等待B完成初始化,而B的创建又依赖A已就绪。若依循线性执行的直觉,这无异于一道无解的死锁命题。然而Spring并未退让,它以三级缓存为舟,以早期暴露为桨,在实例化与初始化之间劈开一道精微的时间缝隙——这并非妥协,而是一次对“完整性”定义的勇敢重写。源码逐行解析至getSingleton方法深处可见:当A进入createBean流程,在其实例化完成、属性填充尚未开始之际,Spring便将其“早期引用”(early reference)通过ObjectFactory封装,提前注册进singletonFactories;待B在依赖注入阶段尝试getBean("a")时,该工厂即被触发,返回一个尚未初始化但已具象的A对象,从而打破闭环。此时的A虽未经历populateBeaninitializeBean,却已能作为合法依赖参与构建。这一设计不回避循环,而是将“依赖可用性”与“状态完备性”解耦——它承认系统本就生长于关系之网,因而选择在最克制的节点释放最必要的信任。所谓解决,并非消灭依赖,而是为相互凝望的两个灵魂,预留一扇半开的门。

4.2 三级缓存机制详解:如何保证单例Bean的唯一性与完整性

singletonObjectsearlySingletonObjectssingletonFactories——这三个键名朴素的ConcurrentHashMap,共同构筑了Spring单例管理中最富张力的三角结构。它们并非并列的存储仓库,而是一组严格时序化的生命快照:singletonObjects存放完全初始化完毕、可对外服务的终态单例;earlySingletonObjects缓存已实例化但未填充属性的“半成品”,供循环依赖场景即时取用;singletonFactories则更进一步,仅保存生成早期引用的ObjectFactory,是尚未落地的承诺本身。三者协同运作,由addSingletonaddEarlySingletonObjectaddSingletonFactory三个原子操作精准调控,所有写入均受synchronized块保护,读取则依托ConcurrentHashMap的无锁特性实现高并发安全。源码逐行解析至此,我们终于看清:Spring对“唯一性”的捍卫,从不寄望于一把全局锁,而在于对生命周期每一毫秒的精确标注与分层托管——它允许一个Bean在不同阶段以不同形态存在,却绝不允许多个终态实例共存于singletonObjects这三级缓存,不是冗余的备份,而是时间维度上的精密分镜;它让“单例”二字,既承载语义的绝对性,也保有实现的弹性与尊严。

五、Bean的生命周期终结与销毁机制

5.1 Bean的销毁流程:destroy方法与DisposableBean接口

当一个单例Bean在Spring容器中走完其被创建、填充、初始化、注册的全部旅程,它便如一位被郑重加冕的臣子,立于singletonObjects的圣殿之中——然而,真正的庄严,不仅在于登临,更在于退场时的秩序与敬意。Spring并未将销毁视为创建的简单逆过程,而是一场有据可循、有章可依、有钩可挂的终局仪式。DisposableBean接口与destroy-method属性,正是这场仪式的两枚信物:前者是契约式的主动承诺,后者是配置化的被动声明。源码逐行解析至AbstractBeanFactorydestroySingletons调用链可见,当容器进入关闭流程,DefaultListableBeanFactory会遍历已注册的单例名称,对每个Bean执行destroyBean——它首先检查是否实现DisposableBean,若是,则调用destroy();继而解析destroy-method配置,通过反射触发用户自定义的清理逻辑。尤为关键的是,这一过程严格遵循“后创建、先销毁”的逆序原则,且所有销毁操作均包裹于统一异常处理机制中,确保一处失败不阻断全局收尾。这不是草率的清除,而是对生命周期完整性的最后确认:一个被Spring郑重托付过的对象,也必须被Spring郑重送别——销毁不是终结的句点,而是定义闭环的最后一笔。

5.2 Spring容器关闭时的资源清理与Bean销毁顺序

容器关闭,从来不是一声令下后的集体湮灭,而是一场静默却严密的退场调度。当AbstractApplicationContext.close()被调用,doClose()方法即刻启动资源释放的倒计时:广播ContextClosedEvent通知监听者,撤销LifecycleProcessor的刷新状态,停止所有实现了SmartLifecycle的组件,最终调用destroyBeans()——这才是单例Bean集体谢幕的真正起点。此时,DefaultListableBeanFactory依据disposableBeans这个LinkedHashMap(其插入顺序即为Bean创建顺序)进行逆向遍历,确保依赖方总在被依赖方之后销毁,从而避免因引用失效导致的空指针或状态错乱。与此同时,DisposableBeanAdapter作为统一适配器,统一封装DisposableBean.destroy()@PreDestroy方法及destroy-method的执行路径,并在每一步注入日志与异常兜底。值得注意的是,该过程全程不依赖任何外部线程池或异步机制,所有销毁动作均同步完成,以保障状态一致性。当最后一行removeSingletonsingletonObjects中抹去那个熟悉的beanName,Spring所交付的,不只是内存的释放,更是一种不可妥协的契约精神——它曾以毫秒级精度守护每一次创建,亦以同等庄重,目送每一个单例走向它应属的终点。

六、总结

本文以源码逐行解析为路径,系统揭示了Spring框架中单例Bean从定义注册到最终销毁的完整生命周期。通过深入追踪AbstractBeanFactoryDefaultListableBeanFactory等核心类的执行逻辑,清晰呈现了BeanDefinition加载、反射实例化、依赖注入、初始化回调、三级缓存协同及循环依赖破局等关键机制。整个过程凸显Spring在保证线程安全、状态可控与语义严谨之间的精妙平衡——单例并非简单“只创建一次”,而是在严格时序约束下,经由多阶段校验、钩子预留与异常兜底所达成的运行时契约。对singletonObjectsearlySingletonObjectssingletonFactories三级缓存的剖析,进一步印证了其设计内核:以时间维度分层托管对象状态,既捍卫单例唯一性,又不牺牲灵活性与可扩展性。源码即文档,流程即思想;唯有回归代码本身,方能真正理解Spring Bean管理的底层智慧与工程魄力。