new 一个对象只有一步。Spring 从容器里取出一个 bean,中间要跑十几个回调,从 BeanNameAware 到 @PostConstruct 再到 afterPropertiesSet。
这些回调是留给外部的扩展点。@Autowired 怎么注进去的、AOP 代理在哪一步包上去的、@PostConstruct 被谁调用的,答案都是"某个回调干的"。把这条链路理顺,这些问题就都能答上来了。
整条链路

入口是 AbstractAutowireCapableBeanFactory 的 doCreateBean,主干就在这个方法里。简化一下:
java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 实例化,拿到一个属性全是 null 的原始对象
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
// 2. 扫描注解元数据,@Autowired 要注入哪里在这里被缓存
applyMergedBeanDefinitionPostProcessors(mbd, bean.getClass(), beanName);
// 3. 提前把 ObjectFactory 放进三级缓存,为循环依赖留后路
if (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 4. 属性填充(@Autowired 在这一步真正注入)
populateBean(beanName, mbd, instanceWrapper);
// 5. 初始化(Aware、@PostConstruct、afterPropertiesSet、AOP)
Object exposedObject = initializeBean(beanName, bean, mbd);
// 6. 注册销毁回调
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}
后面按这六步展开。
实例化
postProcessBeforeInstantiation
createBeanInstance 之前,容器会先问一遍所有 InstantiationAwareBeanPostProcessor:
java
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean; // 短路,后面全不走了
}
postProcessBeforeInstantiation 如果返回了非 null 的对象,这个方法就把它当成最终 bean 直接返回,实例化、属性填充、初始化全都不走了。这个钩子用得很少,但它是理解"为什么有些对象不受 @PostConstruct 影响"的关键。
用哪个构造器
createBeanInstance 这一步就是反射调构造器,出来的对象字段全是 null。麻烦的地方是用哪个构造器。
Spring 通过 determineCandidateConstructors 来挑,AutowiredAnnotationBeanPostProcessor 里的规则是:
text
1. 有标注 @Autowired(required=true) 的构造器 → 用它,而且只能有一个
2. 都没标注,但有且仅有一个有参构造器 → 用它
3. 多个有参构造器且都没标注 → 用无参构造器;没有无参的就报错
所以只写一个带参构造器时可以不加 @Autowired,Spring 认得出来。写两个以上就必须显式标注,否则它不知道该用哪个。
提前扫描注解
java
applyMergedBeanDefinitionPostProcessors(mbd, bean.getClass(), beanName);
@Autowired、@Value、@Resource 要注入哪些字段、哪些方法,在这里被扫出来缓存成 InjectionMetadata。真正注入的时机是后面的 populateBean,这一步只是提前把活干了,避免每次注入都重新反射扫一遍类。
属性填充
populateBean 干的事就是给字段赋上依赖:
java
protected void populateBean(String beanName, BeanDefinition mbd, BeanWrapper bw) {
// 1. 给 InstantiationAwareBeanPostProcessor 一个机会,返回 false 可以跳过整个属性填充
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
// 2. @Autowired / @Resource 注入走这里
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
}
// 3. XML 里配的 <property> 注入走这里
applyPropertyValues(beanName, mbd, bw, pvs);
}
注解注入和 XML 注入的区别就在这。注解是 BeanPostProcessor 处理的(AutowiredAnnotationBeanPostProcessor 管 @Autowired/@Value,CommonAnnotationBeanPostProcessor 管 @Resource),XML 的 <property> 是容器自己处理的。
如果这一步里拿不到某个依赖,而这个依赖又反过来依赖当前 bean,就构成循环依赖,会抛 BeanCurrentlyInCreationException。
初始化
initializeBean 是整条链路里回调最密的一段:
java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
// 1. Aware 回调
invokeAwareMethods(beanName, bean);
// 2. BeanPostProcessor 的前置回调(@PostConstruct 就在这一步被调)
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 3. afterPropertiesSet + init-method
invokeInitMethods(beanName, wrappedBean, mbd);
// 4. BeanPostProcessor 的后置回调 ← AOP 代理在这生成
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
Aware 回调
Aware 是一组接口,实现它就能拿到容器内部的对象。顺序是固定的:
text
BeanNameAware.setBeanName() → bean 的名字
BeanClassLoaderAware.setBeanClassLoader() → 类加载器
BeanFactoryAware.setBeanFactory() → BeanFactory
这三个在 invokeAwareMethods 里直接调。另一组在 ApplicationContextAwareProcessor 里调,它是注册在列表最前面的 BeanPostProcessor:
text
EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware.setApplicationContext() → 拿 ApplicationContext
所以完整顺序是 BeanNameAware → BeanClassLoaderAware → BeanFactoryAware → 后面那一组,最后才是 ApplicationContextAware。
BeanPostProcessor 的两个回调
BeanPostProcessor 是整个生命周期里最有用的扩展点,核心就两个方法:
java
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; }
default Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }
}
BeforeInitialization 跑在 @PostConstruct 那个位置附近,容器里现成的实现有 ApplicationContextAwareProcessor(处理上面那组 Aware)和 CommonAnnotationBeanPostProcessor(调用 @PostConstruct)。
AfterInitialization 更关键,AOP 代理就是在这里生成的。AbstractAutoProxyCreator 的 postProcessAfterInitialization 判断这个 bean 需不需要被代理,需要的话返回一个代理对象,把原始对象替换掉:
@Transactional、@Async、@Cacheable能生效,靠的就是AbstractAutoProxyCreator在postProcessAfterInitialization里把原始对象换成了代理对象
这里也是"同类内部方法调用 @Transactional 不生效"的原因,调用的入口根本没经过代理。
afterPropertiesSet 和 init-method
invokeInitMethods 里按顺序调两个东西:
java
protected void invokeInitMethods(String beanName, Object bean, RootBeanDefinition mbd) {
// 1. 实现了 InitializingBean 接口的
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 2. 配置里指定的 init-method(@Bean(initMethod=...) 或 XML 的 init-method)
if (mbd != null && StringUtils.hasLength(mbd.getInitMethodName())) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
两个都能做"属性填充完之后"的初始化,选哪个基本看习惯。afterPropertiesSet 是接口实现,方法名固定;init-method 不侵入代码,改不了的第三方类库只能用它。
三者(@PostConstruct、afterPropertiesSet、init-method)的执行顺序是 @PostConstruct → afterPropertiesSet() → init-method,为什么是这个顺序放到后面「几个容易混的点」那节说。
销毁
容器关闭(context.close() 或者 JVM 的关闭钩子)时走销毁。前提是这一步注册过:
java
registerDisposableBeanIfNecessary(beanName, bean, mbd);
只有单例、或者实现了销毁方法的 bean 才会被登记。销毁顺序和初始化是对称的:
text
@PreDestroy (DestructionAwareBeanPostProcessor 调用)
→ DisposableBean.destroy()
→ destroy-method
原型(prototype)的 bean 容器不管销毁,容器不持有它的引用,用完之后得自己负责释放。这一点经常被忽略。
把顺序跑一遍
把所有回调都实现一遍,输出顺序是这样的:
java
@Component
public class LifecycleBean implements BeanNameAware, BeanFactoryAware,
ApplicationContextAware, InitializingBean, DisposableBean {
public LifecycleBean() { System.out.println("1. 构造器"); }
@Autowired public void setDep(Dep dep) { System.out.println("2. @Autowired 属性填充"); }
@Override public void setBeanName(String name) { System.out.println("3. BeanNameAware"); }
@Override public void setBeanFactory(BeanFactory bf) { System.out.println("4. BeanFactoryAware"); }
@Override public void setApplicationContext(ApplicationContext ctx) { System.out.println("5. ApplicationContextAware"); }
@PostConstruct public void postConstruct() { System.out.println("6. @PostConstruct"); }
@Override public void afterPropertiesSet() { System.out.println("7. afterPropertiesSet"); }
public void initMethod() { System.out.println("8. init-method"); }
@PreDestroy public void preDestroy() { System.out.println("9. @PreDestroy"); }
@Override public void destroy() { System.out.println("10. DisposableBean.destroy"); }
public void destroyMethod() { System.out.println("11. destroy-method"); }
}
text
1. 构造器
2. @Autowired 属性填充
3. BeanNameAware
4. BeanFactoryAware
5. ApplicationContextAware
6. @PostConstruct
7. afterPropertiesSet
8. init-method
--------- 容器关闭 ---------
9. @PreDestroy
10. DisposableBean.destroy
11. destroy-method
BeanPostProcessor 的两个回调分别落在 6 之前和 8 之后。
几个容易混的点
BeanPostProcessor 和 BeanFactoryPostProcessor
名字很像,管的东西完全不一样:
BeanFactoryPostProcessor |
BeanPostProcessor |
|
|---|---|---|
| 作用对象 | BeanDefinition(还没实例化的配置) |
bean 实例 |
| 时机 | bean 实例化之前 | bean 初始化前后 |
| 典型实现 | ConfigurationClassPostProcessor(解析 @Configuration/@Component) |
AutowiredAnnotationBeanPostProcessor、AbstractAutoProxyCreator |
前者改的是还没实例化的 BeanDefinition,后者改的是已经造出来的 bean 实例。所以在 refresh() 里,BeanFactoryPostProcessor 比 BeanPostProcessor 先执行。
循环依赖和三级缓存
doCreateBean 里那句 addSingletonFactory 就是为循环依赖准备的,位置在 populateBean 之前。
容器里有三个 map:
java
singletonObjects // 一级:完整的单例 bean
earlySingletonObjects // 二级:实例化了但还没填充属性的半成品
singletonFactories // 三级:ObjectFactory,用来生成早期引用(可能是代理)
A 依赖 B、B 依赖 A 时:A 实例化完,先把一个 ObjectFactory 放进三级缓存,然后去填充 B;B 需要 A,走 getSingleton,一级二级都没有,从三级缓存拿到 ObjectFactory,调 getObject() 拿到 A 的早期引用,放进二级缓存,把三级缓存里的删掉;B 填充完、初始化完,A 再继续填充 B,然后自己也初始化完,进一级缓存。
这里我们想一个问题,为什么是三级而不是两级?答案是为了 AOP。如果只有两级缓存,往二级里放的只能是 A 的原始对象。但 A 可能需要被代理(比如带 @Transactional),B 注入的就必须是代理对象,不能是原始对象。三级缓存里的 ObjectFactory 把这个决定推迟到"真的有人需要早期引用"的那一刻,getEarlyBeanReference 会在这时候判断要不要提前创建代理。
换句话说,三级缓存省下的不是一次创建,而是"到底要不要提前造代理"这个决定的时机。
AbstractAutoProxyCreator 实现了这个钩子,它把生成的代理记进 earlyProxyReferences,后面 postProcessAfterInitialization 里发现已经代理过了就不再包一层,避免生成两个代理。
两个限制要记住:
- 只能解决单例 + setter/字段注入。构造器注入解决不了,因为
addSingletonFactory在构造器跑完之后才执行,构造器阶段还没法把自己暴露出去 - Spring Boot 2.6 之后默认禁止循环依赖(
spring.main.allow-circular-references=false)。这套机制知道就行,正经做法还是把依赖拆开
FactoryBean 和 BeanFactory
BeanFactory 是容器本身;FactoryBean 是一个用来自定义 bean 创建逻辑的 bean,实现 getObject() 就能接管"这个 bean 怎么造"。
从容器里 getBean("x") 拿到的是 getObject() 的产物,想拿 FactoryBean 本身要加前缀:
java
context.getBean("myFactoryBean"); // getObject() 的返回值
context.getBean("&myFactoryBean"); // FactoryBean 自己
MyBatis 的 MapperFactoryBean、Spring 的 ProxyFactoryBean 都是这个套路。它的产物也会走一遍 postProcessAfterInitialization,所以 AOP 在 FactoryBean 产出的对象上一样生效。
@PostConstruct、afterPropertiesSet、init-method 的顺序
text
@PostConstruct → afterPropertiesSet() → init-method
@PostConstruct 能排在最前面,是因为它不是容器直接调的,而是由 CommonAnnotationBeanPostProcessor 在 postProcessBeforeInitialization 里调的,这个位置天然在 invokeInitMethods 前面。
afterPropertiesSet 是 invokeInitMethods 里的第一句,init-method 是第二句,这个顺序写死在代码里。
顺带一提,@PostConstruct 由 BeanPostProcessor 触发这件事有个副作用,自定义的、没实现 PriorityOrdered/Ordered 的 BeanPostProcessor,它的 postProcessBeforeInitialization 会排在 @PostConstruct 后面。想让自定义回调在 @PostConstruct 之前动手,得实现 Ordered 把顺序提前。