《Spring 源码现形记》第 2 篇 | 本文源码基于 Spring Framework 6.2.x 分支
面试官:"Bean 的生命周期,说说都有哪些阶段?"
候选人(熟练背诵):"实例化、属性赋值、初始化……还有销毁。"
面试官点点头,追问:"那 @Autowired 注入、@PostConstruct、InitializingBean、AOP 代理,这四件事的先后顺序是什么?谁在谁前面?"
候选人:……
这就是 2026 年面试的典型路数:谁都背过"Bean 生命周期",但面试官要的是你把顺序讲对。因为顺序背后是源码——顺序答错了,说明你只是背了张流程图,没读过代码。
这一篇,我们把 Bean 从"图纸"变成"成品"的每一步,按源码逐一归位。
| 旧问法(背就完事) | 新问法(要真读过源码) |
|---|---|
| Bean 生命周期有哪些阶段? | 实例化、属性填充、初始化、AOP 增强,源码里谁先谁后? |
@PostConstruct 和 afterPropertiesSet 什么区别? |
它们在 initializeBean 里分别由哪一步触发? |
| Aware 接口有哪些? | 为什么 BeanFactoryAware 由 Spring 直接调用,ApplicationContextAware 却要靠一个 BPP? |
Bean 的创建入口是 AbstractAutowireCapableBeanFactory.doCreateBean()(上一篇章1 讲到的第 11 步 preInstantiateSingletons 最终会走到这里)。去掉异常处理和循环依赖的细节,主干只有几行:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// ① 实例化:反射造出对象,此时依赖全是 null
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
// ② 收集注入元数据(@Autowired/@Value 标记的字段/方法)
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
// ③ 提前暴露"早期引用"——这是解决循环依赖的关键(篇3 细讲)
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// ④ 属性填充:@Autowired 在这一步完成注入
populateBean(beanName, mbd, instanceWrapper);
// ⑤ 初始化:Aware、@PostConstruct、init-method、AOP 代理全在这
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}是不是和你想的一样?实例化 ≠ 初始化,这是本题第一个坑,后面单独说。先把四步逐个拆开。
"实例化"只干一件事:把对象 new 出来。此刻对象里的依赖字段全是 null。
createBeanInstance 内部按优先级依次判断:
Supplier回调(如果注册了);factoryMethod(也就是你的@Bean方法);determineConstructorsFromBeanPostProcessors()——交给@Autowired构造器;- 最后兜底:
instantiateBean(),用无参构造反射创建。
所以"构造器注入为什么被官方推荐",在源码层的答案是:它有专门的选择逻辑(ConstructorResolver 会按类型匹配、真有多义再按 @Primary/@Qualifier 裁决),而字段注入只能等对象造好后再反射塞进去。
populateBean 的主干(6.2.x):
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
// 给 InstantiationAwareBeanPostProcessor 一次"属性填充前"的机会
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return; // 返回 false,直接跳过整个属性填充
}
}
// 关键一步:@Autowired / @Resource 注入就发生在这里
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
...
}
// XML 时代的 <property> 值在这里落地
applyPropertyValues(beanName, mbd, bw, pvs);
}管 @Autowired 的 AutowiredAnnotationBeanPostProcessor、管 @Resource 的 CommonAnnotationBeanPostProcessor,都是在 postProcessProperties 这一步被调用的。
记住这句话:走到这里,对象已经存在了,只是依赖刚被"塞"进去。
这是本篇的核心。initializeBean 全文不长,但每一步都是面试题:
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
// ① Aware 回调(只有 3 个!)
invokeAwareMethods(beanName, bean);
// ② 初始化前:@PostConstruct 在这里执行
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// ③ 初始化:InitializingBean + 自定义 init-method
invokeInitMethods(beanName, wrappedBean, mbd);
// ④ 初始化后:AOP 代理在这一步生成
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}拆开看:
① invokeAwareMethods:源码里只处理三个接口——BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。高频追问来了:那 ApplicationContextAware、EnvironmentAware 呢?答案是它们不在这里,而是由 ApplicationContextAwareProcessor(一个 BeanPostProcessor)在第 ② 步执行的。这就是"为什么官方说 Aware 系列的时机不完全一样"的源码依据。
② applyBeanPostProcessorsBeforeInitialization:所有 BPP 的"初始化前"方法依次执行。@PostConstruct 标注的方法,就由 CommonAnnotationBeanPostProcessor 在这一步调用。
③ invokeInitMethods:先判断是否实现 InitializingBean,是就调 afterPropertiesSet();再执行配置里声明的自定义 init-method。
④ applyBeanPostProcessorsAfterInitialization:AOP 代理在这里诞生。
为什么说 AOP 代理是第四步?看 AbstractAutoProxyCreator.postProcessAfterInitialization():
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyBeanReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey); // 需要就生成代理
}
}
return bean;
}wrapIfNecessary 里做判断:这个 Bean 有没有匹配的切面?有就 createProxy 生成代理对象,没有就原样返回。
所以完整顺序是:
构造方法 → @Autowired 注入 → Aware(3 个) → @PostConstruct → afterPropertiesSet → init-method → AOP 代理
背下这条链,面试官问"谁先谁后"就稳了。
这是本篇最值钱的一句话:
AOP 代理在所有初始化动作都完成之后才生成,所以容器最终暴露给你的,是代理对象,不是原始对象。
这句话直接解释了一个经典问题:为什么循环依赖时,B 注入的 A 可能是个"半成品代理"?
因为在 A 的实例化完成后、初始化完成前,为了打破循环,A 会被提前暴露(getEarlyBeanReference)给 B。如果 A 恰好需要 AOP 代理,这个"早期引用"就可能是提前生成的代理。而如果 A 根本不需要代理,早期引用就是原始对象——同一个机制,两种结果,这也是三级缓存里 ObjectFactory 存在的原因。
这个坑我们留到下一篇《三级缓存与循环依赖》一次讲透。
| 时机 | 扩展点接口 | 谁在用 |
|---|---|---|
| 实例化前 | InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation |
可返回代理,直接短路掉实例化 |
| 选择构造器 | SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors |
@Autowired 构造器 |
| 实例化后、注入前 | postProcessAfterInstantiation |
可返回 false 跳过属性填充 |
| 属性填充(注入) | postProcessProperties |
@Autowired / @Resource |
| 合并定义后 | MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition |
收集注入元数据(@Autowired/@Value) |
| 初始化前 | BeanPostProcessor.postProcessBeforeInitialization |
@PostConstruct、ApplicationContextAwareProcessor |
| 初始化 | InitializingBean / init-method |
自定义初始化逻辑 |
| 初始化后 | postProcessAfterInitialization |
AOP 代理 |
| 步骤 | 方法 | 干什么 | 记忆锚点 |
|---|---|---|---|
| ① 实例化 | createBeanInstance |
反射造对象,依赖还是 null | 选构造器 |
| ② 属性填充 | populateBean |
注入依赖 | @Autowired 在这 |
| ③ 初始化 | initializeBean |
Aware → @PostConstruct → afterPropertiesSet → init-method |
四小步顺序 |
| ④ AOP 增强 | applyBeanPostProcessorsAfterInitialization |
生成代理 | 最晚发生 |
- 一个类里同时写:无参构造、
@Autowired构造器、@PostConstruct方法、afterPropertiesSet、自定义init-method——每个都打日志,跑一遍看真实顺序,和我上面那条链对一对; - 把
@PostConstruct方法里的字段打印出来,确认此时@Autowired已经注入完成; - 在
@PostConstruct和afterPropertiesSet里各System.out.println(this.getClass())——如果这个 Bean 有切面,两次打印的类名可能已经在悄悄变化(代理在最后一刻才套上)。
A 依赖 B,B 依赖 A,Spring 到底怎么把它们造出来?三级缓存各存什么、ObjectFactory 为什么要延迟生成、构造器注入为什么救不了——下一篇:《三级缓存与循环依赖:为什么构造器注入救不了》。




