我们写的 Bean 最终都被放在了 Spring IoC 容器 中进行管理。
但“容器”这个词有点抽象。我们可以把它想象成一个高度组织化的内存仓库。这个仓库不是一个简单的 Map,而是一个由多个精密部件组成的系统。
让我们深入这个“仓库”内部,看看 Bean 到底被放在哪里,以及是如何被管理的。
1. 核心管理者:ApplicationContext
我们通常与之交互的 Spring 容器,就是 ApplicationContext 接口的实例。你可以把它看作是这个仓库的“总经理”。它提供了所有对外服务,比如 context.getBean(...)。
但这位“总经理”并不亲自干活,它把具体的存储和管理工作委托给了一个核心组件。
2. 真正的仓库:BeanFactory (特别是 DefaultListableBeanFactory)
ApplicationContext 内部包含了(或者说继承了)一个更底层的容器,叫做 BeanFactory。在绝大多数情况下,这个 BeanFactory 的具体实现是 DefaultListableBeanFactory。
DefaultListableBeanFactory 就是那个真正的、存储所有 Bean 的核心仓库。 所有的 Bean 定义和单例实例最终都存放在这个类的一系列 Map 结构中。
3. 仓库里的两个关键区域
在这个 DefaultListableBeanFactory 仓库里,存放的并不仅仅是最终的对象。它主要管理两样东西:
- 区域一:Bean 的“配方”——
BeanDefinition - 区域二:Bean 的“成品”—— 单例对象实例(Singleton Objects)
区域一:Bean 的“配方”清单 (BeanDefinitionMap)
当 Spring 启动时,它首先会进行组件扫描,或者解析 XML/Java 配置。这个过程并不是立刻创建所有对象。
相反,它会把每个找到的 Bean 的所有信息,封装成一个叫做 BeanDefinition 的对象。这个对象就是 Bean 的“配方”或“图纸”,里面记录了:
- Bean 的类名 (
"com.myapp.service.MyUserService") - Bean 的作用域 (Scope:
singleton还是prototype等) - 是否懒加载 (
lazy-init) - 依赖哪些其他的 Bean (Dependencies)
- 初始化和销毁方法是什么
- …等等所有元数据。
所有这些 BeanDefinition 对象,都被存储在一个 ConcurrentHashMap 中,这个 Map 通常叫做 beanDefinitionMap。
Map<String, BeanDefinition>
- Key: Bean 的名字(比如 “myUserService”)。
- Value: 对应的
BeanDefinition对象(“配方”)。
这个区域就像是图书馆的图书索引卡系统,记录了图书馆里所有书的信息,但书本身还没从书架上拿下来。
区域二:成品的“单例货架” (SingletonObjects Cache)
对于作用域为 singleton (这是默认作用域) 的 Bean,当它们第一次被创建后,为了性能和复用,其实例会被缓存起来。
这个缓存区就是存放“成品”的地方。它主要由三个 Map 组成,被合称为三级缓存,这是 Spring 用来解决循环依赖问题的精妙设计。但我们可以简化理解为:
-
一级缓存 (Singleton Objects):
singletonObjects- 这是一个
ConcurrentHashMap,存放的是已经完全初始化好的、立即可用的单例 Bean 实例。 - 当你调用
context.getBean("myUserService")时,Spring 会首先检查这个一级缓存。如果找到了,直接返回,速度极快。 Map<String, Object>- Key: Bean 的名字 (“myUserService”)。
- Value:
MyUserService的那个唯一的实例对象。
- 这是一个
-
二级缓存 (Early Singleton Objects):
earlySingletonObjects- 存放的是已经实例化但尚未完全初始化(比如还没进行属性注入)的 Bean。主要用于解决循环依赖。
-
三级缓存 (Singleton Factories):
singletonFactories- 存放的是一个“工厂对象”,这个工厂的
getObject()方法能够返回一个早期的 Bean 实例。同样是为了解决循环依赖,特别是涉及 AOP 代理的循环依赖。
- 存放的是一个“工厂对象”,这个工厂的
对于我们日常使用来说,最重要的是理解一级缓存 singletonObjects。我们写的绝大多数 @Service、@Repository 等 Bean,最终都会以唯一实例的形式,安安稳稳地待在这个 Map 里,等待被其他 Bean 注入或被我们调用。
总结
把整个流程用一个图书馆的比喻串起来:
-
开馆准备 (Spring 启动):
- 图书馆管理员(
BeanFactory)整理了一份完整的图书目录(beanDefinitionMap)。目录上写着每本书(Bean)的详细信息(BeanDefinition)。
- 图书馆管理员(
-
读者借书 (第一次调用
getBean):- 一个读者想借《Java 编程思想》这本书(
myBookService)。 - 管理员首先查看“热门借阅/参考书架”(一级缓存
singletonObjects),看上面有没有这本书。 - 没有。
- 管理员于是去查图书目录(
beanDefinitionMap),找到了这本书的详细信息。 - 他根据信息去书库(JVM 堆)里把这本书的原版(
new MyBookService())找了出来,并盖上“图书馆藏”的印章(初始化、注入依赖等)。 - 为了方便下一个读者,他把这本书的复印本(Bean 实例的引用)放在了“热门借阅/参考书架”(一级缓存
singletonObjects)上。 - 最后,他把这本书交给了读者。
- 一个读者想借《Java 编程思想》这本书(
-
下一个读者借同一本书:
- 管理员直接在“热门借阅/参考书架”(一级缓存)上看到了这本书,直接拿给读者,无需再去翻目录和跑书库。
所以,你写的 Bean 最终被DefaultListableBeanFactory 这个核心仓库,以配方(BeanDefinition) 和 成品(Singleton Instance) 两种形态,存放在其内部不同的 in-memory Map 中进行统一、高效的管理。
2942



被折叠的 条评论
为什么被折叠?



