Spring 源码探险:容器之心
为什么是 Spring 容器?—— 一切的起点与核心
我们每天都在用 @Autowired
,信手拈来,如同呼吸般自然。但你是否曾在夜深人静时扪心自问:它背后到底发生了什么?Spring 就像一个无所不知的先知,总能准确地找到我们需要的那个 Bean,不多不少,刚刚好。它是如何知道该注入什么,又是在何时完成注入的?
忘掉那些官方文档里画了又画的 UML 图,忘掉那些翻来覆去讲的理论概念。今天,我们不谈“是什么”,只究“怎么做”。我们将扮演一个冷酷的 Debugger,带上我们的显微镜,以源码为唯一的语言,以方法调用链为唯一的路径,单步跟进 Spring 容器从一个平平无奇的 Java 对象,蜕变为一个管理着整个应用生命周期的“上帝容器”的恢弘史诗。
本次探险,我们将从最经典的一行代码 new ClassPathXmlApplicationContext("bean.xml")
出发,一头扎进 Spring 的主动脉——refresh()
方法,在其中探索生命周M期的奥秘,最终在 getBean()
的终点见证一个 Bean 的诞生。
这是一场硬核的旅程,准备好了吗?🚀
一 、创世纪——ApplicationContext
的诞生前夜
一切的伟大,都源于一个微不足道的 new
。
// 我们的起点
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
1.1 起点:new ClassPathXmlApplicationContext("bean.xml")
当我们写下这行代码时,构造函数的世界里发生了什么?
- 源码路径:
org.springframework.context.support.ClassPathXmlApplicationContext
跟进去,你会发现一个 this()
调用链。它会一路调用父类的构造函数,像一条奔涌的河流,最终汇入 AbstractApplicationContext
的构造函数中。这是一个经典的设计模式:模板方法的体现,父类定义好骨架,子类负责实现细节。
在 AbstractApplicationContext
的构造函数里,有两件至关重要的事情发生:
资源定位: 我们传入的
"bean.xml"
是一个简单的字符串,Spring 如何理解它,并找到它?源码追踪:
setConfigLocations(configLocations)
->getPathMatcher()
->PathMatchingResourcePatternResolver
。核心解读: Spring 将一切外部信息都抽象为
Resource
。ClassPathXmlApplicationContext
内部会创建一个PathMatchingResourcePatternResolver
(路径匹配资源解析器),它能理解classpath*:
、ant
风格(如com/**/service.xml
)等复杂路径。它将字符串路径转换为一个或多个Resource
对象,这体现了 Spring 对底层资源的统一抽象和封装,无论它是来自文件系统、classpath 还是网络。“发令枪”
refresh()
: 在构造函数的最后一步,也是最关键的一步,代码会调用refresh()
方法。
// AbstractApplicationContext.java
public AbstractApplicationContext() {
// ... 其他准备工作
}
// ClassPathXmlApplicationContext.java
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
// ...
setConfigLocations(configLocations); // 设置资源路径
refresh(); // 关键调用!
}
为什么不等对象完全构造好再手动调用初始化?因为 ApplicationContext
的职责就是作为一个已经就绪的、可用的容器。它的诞生,必须伴随着自身的初始化完成。refresh()
就是这声“发令枪”,一旦打响,容器的创世之旅便正式开始。
二 refresh()
—— Spring 容器的“核心引擎”与“生命周期”
refresh()
方法是 Spring 容器的心脏,是整个 IoC 容器初始化和生命周期管理的绝对核心。它位于 AbstractApplicationContext
中,包含了 13 个清晰的步骤,如同一份构建蓝图,精确地定义了容器的启动流程。
让我们深入这座“引擎室”,逐一拆解这 13 个步骤。
- 源码路径:
org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 准备阶段
prepareRefresh();
// 2. 获取新的 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备 BeanFactory
prepareBeanFactory(beanFactory);
try {
// 4. BeanFactory 的后置处理
postProcessBeanFactory(beanFactory);
// 5. 调用 BeanFactoryPostProcessors
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 BeanPostProcessors
registerBeanPostProcessors(beanFactory);
// 7. 初始化 MessageSource (国际化)
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 特殊 Bean 的初始化 (留给子类实现)
onRefresh();
// 10. 注册事件监听器
registerListeners();
// 11. 实例化所有非懒加载的单例
finishBeanFactoryInitialization(beanFactory);
// 12. 完成刷新,发布事件
finishRefresh();
}
catch (BeansException ex) {
// ... 异常处理
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
// ... 重置缓存
resetCommonCaches();
}
}
}
2.1 第一阶段:容器的准备与扫描 (步骤 1-3)
这是“备料”阶段,为后续的加工做准备。
prepareRefresh()
: 设置容器的启动时间、激活状态标志,并对一些属性进行早期验证。非常直白。obtainFreshBeanFactory()
: 分水岭! 这是BeanFactory
诞生的时刻。核心解读: 此方法会先销毁旧的
BeanFactory
(如果存在),然后创建一个全新的DefaultListableBeanFactory
。接着,它会调用loadBeanDefinitions()
,这是一个抽象方法。对于我们的ClassPathXmlApplicationContext
,它会委托XmlBeanDefinitionReader
去解析bean.xml
文件。XmlBeanDefinitionReader
的工作: 它会逐行读取 XML,将每一个<bean>
标签的信息(id
,class
,scope
,<property>
等)解析并封装成一个BeanDefinition
对象。BeanDefinition
是什么? 它是 Spring Bean 的“蓝图”或“档案”。它不是 Bean 实例本身,而是描述了 Bean 应该如何被创建的元数据。注册: 最终,这些
BeanDefinition
对象被注册到一个名为beanDefinitionMap
的ConcurrentHashMap
中。key
是beanName
,value
是BeanDefinition
对象。至此,IoC 容器的数据基础已经奠定。容器知道了它需要管理哪些 Bean,以及如何创建它们。
prepareBeanFactory(beanFactory)
: 为BeanFactory
添加“佐料”。- 核心解读: 此时的
BeanFactory
还很“素”。这一步会给它设置一些默认组件,比如ClassLoader
,表达式解析器SpelExpressionParser
,以及添加几个特殊的BeanPostProcessor
(我们后面会详谈)。
- 核心解读: 此时的
2.2 第二阶段:BeanFactory
的“增强”与“扩展点” (步骤 4-5)
这是 Spring 提供给我们的第一个黄金扩展点。
postProcessBeanFactory(beanFactory)
: 这是一个经典的模板方法,设计为子类去重写。例如,AnnotationConfigApplicationContext
就会在这里添加处理@Configuration
注解的后置处理器。AbstractApplicationContext
在这里提供了一个空的实现,其目的就是为了让子类去重写,从而在BeanFactory
初始化之后、任何 Bean 实例化之前,添加一些子类特有的功能。例如,WebApplicationContext
就会重写此方法,注册request
、session
等 Web 专属的scope
,并添加对ServletContext
的感知等。invokeBeanFactoryPostProcessors(beanFactory)
: 极其重要!这个方法的执行逻辑,并非一个简单的循环调用。其内部由一个名为
PostProcessorRegistrationDelegate
的静态代理类来精心编排,整个过程充满了优先级排序和阶段划分,其复杂和严谨程度,远超想象。首先,Spring 在此阶段处理两种类型的处理器,它们的能力和职责有本质区别:
BeanDefinitionRegistryPostProcessor
(BDRPP): 这是BeanFactoryPostProcessor
的一个子接口。它更强大,除了拥有BeanFactoryPostProcessor
的所有能力外,还额外拥有一个核心方法:postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
。- 核心能力:可以动态地注册、修改、移除
BeanDefinition
。它能改变BeanFactory
中BeanDefinition
的“数量和结构”。 - 典型代表:
ConfigurationClassPostProcessor
,它负责扫描@Configuration
类,解析@ComponentScan
和@Bean
,然后将成百上千个新的BeanDefinition
注册到容器中。
- 核心能力:可以动态地注册、修改、移除
BeanFactoryPostProcessor
(BFPP):- 核心能力:只能读取和修改已存在的
BeanDefinition
的元数据(比如修改某个属性值)。它不能增删BeanDefinition
。它只能改变BeanDefinition
的“内容”。 - 典型代表:
PropertySourcesPlaceholderConfigurer
,它负责将${...}
占位符替换为真实的属性值。
- 核心能力:只能读取和修改已存在的
核心结论:BDRPP
是“地图绘制者”,能画出新的区域;BFPP
是“地图美化者”,只能在已有的地图上进行标注。因此,Spring 必须先执行所有 BDRPP
,再执行 BFPP
,确保地图都画好了,才能开始美化。
在方法的一开始,定义了几个关键的 List
,用于在处理过程中分类存放不同类型的处理器:
// 用于存放已执行的 BeanDefinitionRegistryPostProcessor 的名字,防止重复执行
Set<String> processedBeans = new HashSet<>();
// 存放硬编码(或由 ApplicationContext 直接添加)的 BDRPP
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 存放硬编码的普通 BFPP
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
执行流程的逐行解剖
步骤 1:处理硬编码的 BeanDefinitionRegistryPostProcessor
方法首先会处理那些不是作为 Bean 定义,而是被程序直接 addBeanFactoryPostProcessor()
添加进来的处理器。
// 循环遍历所有硬编码的后处理器
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
// 如果是 BDRPP 类型
BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
// 直接调用其 postProcessBeanDefinitionRegistry 方法
registryProcessor.postProcessBeanDefinitionRegistry(registry);
// 并将其存入 registryProcessors 列表,以便后续调用其 postProcessBeanFactory 方法
registryProcessors.add(registryProcessor);
} else {
// 如果是普通的 BFPP,则先存起来,稍后处理
regularPostProcessors.add(postProcessor);
}
}
步骤 2:处理作为 Bean 定义的 BeanDefinitionRegistryPostProcessor
这是最复杂也最精妙的部分。Spring 需要从 BeanFactory
中找出所有被定义为 Bean 的 BDRPP
,并严格按优先级执行。
// 用于存放当前需要处理的 BDRPP
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 第一优先级:处理实现了 PriorityOrdered 接口的 BDRPP
String[] postProcessorNames = registry.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 获取 Bean 实例并添加到当前处理列表
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 排序并调用
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// 清空当前列表,为下一优先级做准备
currentRegistryProcessors.clear();
// 第二优先级:处理实现了 Ordered 接口的 BDRPP
// ... (代码逻辑与上面类似,只是 isTypeMatch 的判断条件变为 Ordered.class) ...
// ... (排序、调用、清空) ...
// 第三优先级:处理所有其他的 BDRPP
// ... (在一个循环中不断查找和处理,直到没有新的 BDRPP 出现为止) ...
// 这个循环是为了处理在一个 BDRPP 中注册了另一个 BDRPP 的场景
// ... (排序、调用、清空) ...
这个过程的核心是,分三次从容器中捞取不同优先级的 BDRPP
Bean,并立即执行它们的 postProcessBeanDefinitionRegistry
方法。ConfigurationClassPostProcessor
就是在第一优先级(它实现了 PriorityOrdered
)被获取并执行的。
步骤 3:调用所有处理器的 postProcessBeanFactory
方法
至此,所有 BeanDefinition
的注册工作已经全部完成,“地图”已经绘制完毕。现在,开始执行所有 BFPP
的逻辑(包括那些 BDRPP
,因为它们也是 BFPP
)。
// 首先,调用所有已执行过的 BDRPP 的 postProcessBeanFactory 方法
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
// 然后,调用所有硬编码的普通 BFPP 的方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
// 最后,按照与步骤2相同的优先级逻辑,从容器中找出所有普通的 BFPP Bean
// 并调用它们的 postProcessBeanFactory 方法
// ... (处理 PriorityOrdered 的 BFPP -> 处理 Ordered 的 BFPP -> 处理其他的 BFPP) ...
| 执行阶段 | 处理器类型 | 执行顺序 | 核心任务 | 经典案例 |
| ———— | ————————————- | ———————————————————— | ————————————————– | ———————————————————– |
| 阶段一 | BeanDefinitionRegistryPostProcessor
| 1. 硬编码的<br>2. 作为Bean的 (按 PriorityOrdered
-> Ordered
-> 普通 排序) | 注册/修改/移除 BeanDefinition
(改变蓝图结构) | ConfigurationClassPostProcessor
(处理注解配置) |
| 阶段二 | BeanFactoryPostProcessor
| 1. 已执行过的BDRPP<br>2. 作为Bean的 (按 PriorityOrdered
-> Ordered
-> 普通 排序) | 修改 BeanDefinition
的元数据 (改变蓝图内容) | PropertySourcesPlaceholderConfigurer
(替换${...}
占位符) |
为什么这个设计如此重要?
这个看似复杂的流程,保证了 Spring 容器的极致可扩展性。它确保了:
配置的确定性:所有 Bean 的定义(“有什么 Bean”)都在任何 Bean 属性被修改(“Bean 的内容是什么”)之前被完全确定下来。
功能的正交性:负责“扫描注册”的处理器和负责“属性修改”的处理器被清晰地分离开来,各司其职。
强大的扩展能力:开发者可以编写自己的
BDRPP
或BFPP
,精确地在容器生命周期的这个黄金扩展点介入,实现各种自定义的框架功能,例如动态注册 Bean、加密属性解密等。
@Configuration
的秘密:既然说到@Configuration
注解的后置处理器又提到BeanDefinitionRegistryPostProcessor,那就不得揭秘一下容器是怎么感知@Configuration
,@Bean等注解然后注册到容器中的。
ConfigurationClassPostProcessor
也是一个 BeanFactoryPostProcessor
。它会在这一步扫描所有 @Configuration
类,解析 @Bean
, @Import
, @ComponentScan
等注解,然后将它们也转化为新的 BeanDefinition
注册到容器中我们钻入 ConfigurationClassPostProcessor
的核心方法 postProcessBeanDefinitionRegistry
,看看它是如何“绘制地图”的。processConfigBeanDefinitions
的两大阶段
这个方法的工作可以清晰地分为两个阶段:解析(Parse) 和 加载(Load)。
阶段一:解析(Parse)—— 将注解翻译成内部模型
- 创建解析器:方法内部首先会创建一个
ConfigurationClassParser
。这个解析器是所有解析工作的入口。 - 收集候选者:它会遍历
BeanDefinitionRegistry
中所有已注册的BeanDefinition
,找出那些被标记为@Configuration
的“配置类候选者”。 - 执行解析:调用
parser.parse(candidates)
。parser
会对候选者集合进行循环处理,对每一个配置类,它会调用doProcessConfigurationClass
方法。 doProcessConfigurationClass
的内部细:这是真正的“翻译”工作发生的地方。- 处理
@Conditional
:在处理任何注解前,它会先通过shouldSkip()
方法检查类上的@Conditional
和@Profile
注解,如果条件不满足,整个类都会被跳过。 - 处理
@PropertySource
:解析注解,并将指定的属性文件加载到Environment
中。 - 处理
@ComponentScan
:创建一个ComponentScanAnnotationParser
,它会根据注解指定的包路径,使用ClassPathBeanDefinitionScanner
去扫描,找到所有带@Component
、@Service
、@Repository
等注解的类,并将它们初步解析为BeanDefinition
注册到容器中,同时返回这些类供后续处理。
- 处理
- 处理
@Import
:这是最复杂的部分。 - 如果导入的是另一个
@Configuration
类,则递归调用解析流程。 - 如果导入的是
ImportSelector
,则实例化它并调用其selectImports()
方法,获取一个类名数组,然后对这些类进行递归解析。 - 如果导入的是
ImportBeanDefinitionRegistrar
,则先不执行,而是将它暂存起来,等待后续加载阶段再回调。 - 处理
@Bean
:它不会立刻将@Bean
方法转为BeanDefinition
,而是扫描类中的所有@Bean
方法,将它们的信息封装成BeanMethod
对象,存入ConfigurationClass
对象的beanMethods
集合中。 - 处理接口的
default
方法:还会处理接口中的default
@Bean
方法。
经过这个阶段,所有的注解信息都被翻译并存储在了一系列 ConfigurationClass
对象中。此时,除了 @ComponentScan
扫描到的组件外,@Bean
方法还未成为真正的 BeanDefinition
。
阶段二:加载(Load)—— 将内部模型物化为 BeanDefinition
创建加载器:解析阶段完成后,
processConfigBeanDefinitions
方法会创建一个ConfigurationClassBeanDefinitionReader
。执行加载:调用
reader.loadBeanDefinitions(configClasses)
。这个加载器会遍历第一阶段生成的所有ConfigurationClass
对象集合。loadBeanDefinitionsForConfigurationClass
:对于每一个 ConfigurationClass:加载
@Bean
方法:调用loadBeanDefinitionsForBeanMethod()
,为每个@Bean
方法创建一个ConfigurationClassBeanDefinition
,这是一个特殊的BeanDefinition
,它记录了该 Bean 是由哪个工厂方法(@Bean
方法)和哪个工厂 Bean(@Configuration
类)创建的,然后将其注册到BeanDefinitionRegistry
中。加载
@Import
的ImportBeanDefinitionRegistrar
:回调之前暂存的ImportBeanDefinitionRegistrar
实例,执行其registerBeanDefinitions()
方法,让它可以编程式地注册任意数量的BeanDefinition
。加载
@ImportResource
注解指定的 XML 文件。
至此,ConfigurationClassPostProcessor
的使命才算真正完成。它通过“解析-加载”两步走,将我们书写的、人类易于理解的注解,系统性、结构化地转换为了 Spring IoC 容器能够理解的底层数据结构——BeanDefinition
。
2.3 第三阶段:Bean 的“加工厂”—— BeanPostProcessor
的注册 (步骤 6)
第二个黄金扩展点,为后续所有 Bean 的生命周期埋下伏笔。
registerBeanPostProcessors(beanFactory)
:- 核心解读: 此方法会找出容器中所有实现了
BeanPostProcessor
接口的 Bean,然后将它们实例化并添加到BeanFactory
的beanPostProcessors
列表中。 - 排序: 它还会对这些
BeanPostProcessor
进行排序,遵循PriorityOrdered
>Ordered
> 无序的规则。 BeanPostProcessor
是什么? 它是 Bean 的后置处理器。与BeanFactoryPostProcessor
不同,它干预的是 Bean 实例化过程本身,在 Bean 初始化前后提供切入点。我们熟知的 AOP、@Autowired
的实现都离不开它。
2.4 第四阶段:应用上下文功能准备 (步骤 7-10)
这部分是为 ApplicationContext
相对于 BeanFactory
额外提供的企业级功能做准备。
initMessageSource()
: 初始化国际化支持。initApplicationEventMulticaster()
: 初始化事件广播器。这是一个典型的观察者模式实现,负责管理所有ApplicationListener
并向它们派发事件。onRefresh()
: 一个模板方法,留给子类在特定时机执行特殊 Bean 的初始化。registerListeners()
: 从容器中找出所有ApplicationListener
Bean,并将它们注册到广播器中。
2.5 第五阶段:创世的终章——实例化所有“非懒加载”单例 (步骤 11)
refresh()
中最激动人心的一步!
finishBeanFactoryInitialization(beanFactory)
:- 核心解读: 此方法的核心是调用
beanFactory.preInstantiateSingletons()
。 preInstantiateSingletons()
: 它会遍历beanDefinitionMap
中所有的BeanDefinition
,筛选出所有非抽象、单例且非懒加载的 Bean,然后调用getBean(beanName)
来触发它们的创建。- 这是一个“急切”的动作,确保在容器启动完成后,所有核心的单例 Bean 都已经准备就绪,立即可用。这个调用将直接把我们的源码探险引导至下一章的核心——
getBean()
。
2.6 终曲:发布容器就绪事件 (步骤 12)
finishRefresh()
:- 核心解读: 清理资源,最重要的是发布一个
ContextRefreshedEvent
事件。所有监听了这个事件的ApplicationListener
(比如WebServerStartStopLifecycle
)都会被触发,标志着容器已完全就绪。
至此,refresh()
的 13 个步骤走完。一个功能完备、管理着所有单例 Bean 的 IoC 容器正式诞生!
三 getBean()
—— 一个 Bean 的诞生、依赖与宿命
如果说 refresh()
是宏观的构建过程,那么 getBean()
就是微观的创造过程。它回答了终极问题:一个 Bean 究竟是如何从 BeanDefinition
变成一个活生生的 Java 对象的?
3.1 入口:getBean(String name)
当我们调用 context.getBean("userService")
时,旅程开始了。
- 源码路径:
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(String)
这个方法是一个入口,它会做一些转换和检查,然后将调用委托给核心方法 doGetBean()
。
// AbstractBeanFactory.java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
// ...
}
3.2 核心逻辑:doGetBean
的三级缓存与循环依赖
doGetBean
的逻辑非常复杂,但其精髓在于对单例 Bean 的处理,尤其是三级缓存的设计,这是 Spring 用来解决循环依赖问题的神来之笔。
- 源码路径:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
在 DefaultSingletonBeanRegistry
中,定义了三个 Map:
singletonObjects
(一级缓存):Map<String, Object>
- 用途: 存放已经完整初始化好的单例 Bean(成品)。
getBean
请求首先会检查这里。 earlySingletonObjects
(二级缓存):Map<String, Object>
- 用途: 存放早期暴露的单例 Bean。这些 Bean 已经实例化,但尚未填充属性(半成品)。
singletonFactories
(三级缓存):Map<String, ObjectFactory<?>>
- 用途: 存放用于创建早期暴露 Bean 的工厂 (
ObjectFactory
)。
循环依赖解决流程(以 A 依赖 B,B 依赖 A 为例):
getBean("a")
开始,检查三级缓存,均未命中。- 开始创建 A。首先将一个
ObjectFactory
放入三级缓存singletonFactories
中。这个工厂的作用是,如果有人需要,它可以立刻创建一个 A 的“半成品”对象(通过getEarlyBeanReference
,这里是 AOP 代理介入的关键时机)。 - 对 A 进行属性填充 (
populateBean
)。发现 A 依赖 B,于是触发getBean("b")
。 getBean("b")
开始,检查三级缓存,均未命中。- 开始创建 B。同样,将 B 的
ObjectFactory
放入三级缓存。 - 对 B 进行属性填充,发现 B 依赖 A,于是触发
getBean("a")
。 - 关键点来了!getBean(“a”) 再次被调用。此时:
- 检查一级缓存
singletonObjects
,没有。 - 检查二级缓存
earlySingletonObjects
,没有。 - 检查三级缓存
singletonFactories
,命中了!我们找到了在第 2 步放入的 A 的工厂。 - 通过这个工厂,立刻创建一个 A 的早期引用(如果需要 AOP,此时会创建代理对象),并将这个早期引用从三级缓存中移除,放入二级缓存
earlySingletonObjects
中。 getBean("a")
返回了这个早期引用。B 顺利完成了属性填充,成为一个完整的 Bean。getBean("b")
执行完毕,将完整的 B 对象放入一级缓存singletonObjects
中,并返回。- 回到第 3 步,A 拿到了 B 的实例,也顺利完成了属性填充。
- A 也成为一个完整的 Bean,
getBean("a")
的最外层调用执行完毕,将完整的 A 对象放入一级缓存。
循环依赖被解开!三级缓存的设计核心在于:利用一个对象工厂,将实例化和属性填充这两个步骤分开,允许在对象未完全创建好时,提前暴露一个引用。
3.3 Bean 的生命周期全景图:从 createBean
到销毁
如果缓存中没有,Spring 就会调用 createBean
来从零开始创造一个 Bean。
- 源码路径:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
这是一个 Bean 生命周期的微观缩影:
createBeanInstance()
: 实例化。通过反射调用构造函数,或者工厂方法,new 一个对象出来。此时它只是一个“裸”对象。populateBean()
: 属性填充。这是依赖注入发生的地方。AutowiredAnnotationBeanPostProcessor
等后置处理器会在这里大显身手,扫描对象的所有字段和方法,如果发现@Autowired
注解,就去容器中getBean()
相应的依赖,然后通过反射设置到当前对象上。initializeBean()
: 初始化。这是 Bean 得以“感知”容器,并执行自定义逻辑的地方。invokeAwareMethods()
: 如果 Bean 实现了BeanNameAware
,BeanFactoryAware
等接口,此时会调用这些接口的方法,将容器自身的信息注入给 Bean。applyBeanPostProcessorsBeforeInitialization()
: 执行所有BeanPostProcessor
的postProcessBeforeInitialization
方法。invokeInitMethods()
: 调用 Bean 自己定义的初始化方法(@PostConstruct
注解的方法或afterPropertiesSet
方法或 XML 中的init-method
)。applyBeanPostProcessorsAfterInitialization()
: 执行所有BeanPostProcessor
的postProcessAfterInitialization
方法。这是 AOP 代理发生的关键节点!
3.4 AOP 的切入时机
- 源码路径:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
在 initializeBean
的最后一步,AbstractAutoProxyCreator
(一个 BeanPostProcessor
)会检查当前初始化的 Bean。
- 核心解读: 它会拿着容器中所有的
Advisor
(切面),去和当前 Bean 的所有方法进行匹配。如果发现至少有一个方法匹配成功,就意味着这个 Bean 需要被代理。 - 接着,它会创建一个
ProxyFactory
,根据情况选择 JDK 动态代理(如果目标类实现了接口)或 CGLIB(如果目标类没有实现接口),生成一个代理对象。 - 最后,这个代理对象会替换掉原始的 Bean 对象,并返回给容器。
- 从此以后,任何对这个 Bean 的调用,实际上都是在调用这个代理对象,AOP 的逻辑(
Advice
)也因此得以在目标方法前后执行。
四 、总结 —— 从源码看设计,从设计看思想
当我们从源码的深渊中探出头来,回望整个 Spring 容器,我们看到的不再是零散的注解和配置,而是一幅宏伟的设计蓝图。
4.1 设计模式的盛宴
Spring 容器就是一本活生生的设计模式教科书:
- 工厂模式:
BeanFactory
本身就是最典型的工厂。 - 模板方法模式:
refresh()
,createBean()
等骨架方法,将流程固定,将变化点开放给子类。 - 观察者模式:
ApplicationEvent
和ApplicationListener
构成了 Spring 的事件驱动模型。 - 代理模式: Spring AOP 的核心实现。
- 策略模式:
InstantiationStrategy
(实例化策略)、BeanNameGenerator
(Bean 名称生成策略)等,允许用户在特定环节替换算法。 - 单例模式:
DefaultSingletonBeanRegistry
完美地管理着所有单例 Bean
4.2 Spring 的核心思想
源码之下,是 Spring 深刻的架构思想:
- 控制反转 (IoC): 整个流程都是容器在主动推进,Bean 的创建、依赖的获取,都由容器控制,应用程序只是被动接受。
- 面向切面 (AOP): 以
BeanPostProcessor
为核心,通过代理技术,实现了对业务代码的无侵入式功能增强。 - 面向接口编程: 整个容器充满了扩展点,
BeanFactoryPostProcessor
,BeanPostProcessor
,Aware
接口… 它们共同构建了一个极度灵活、可插拔的系统。 - 万物皆可抽象: 从
Resource
对资源的抽象,到BeanDefinition
对 Bean 的抽象,再到Environment
对环境的抽象,Spring 善于创建上层抽象来屏蔽底层差异,提供统一的视图。
通过这次旅程,我们不再仅仅是 Spring 的使用者,我们成为了它的理解者。当我们再次写下 @Autowired
时,脑海中浮现的将是三级缓存的精妙舞蹈;当我们配置 AOP 时,心中了然的是 BeanPostProcessor
在生命周期末端的悄然一瞥。
这,仅仅是 Spring 探险的第一站。容器是心脏,但 Spring 的世界远不止于此。下一站,我们将深入 AOP 的迷雾,或是探索 Spring MVC 的请求之旅。