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(): 从容器中找出所有ApplicationListenerBean,并将它们注册到广播器中。
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 的请求之旅。