深入理解Spring Bean初始化流程

深入理解Spring Bean初始化流程

一、前言

​ 如果说Spring的IoC容器是一座精密的工厂,那么Bean的创建就是这座工厂最核心的生产流水线。在这条流水线上,一个Bean从无到有,大体经历三个关键工序:实例化(Instantiation)属性填充(Populate)初始化(Initialization)

​ 很多开发者对Bean的实例化和属性注入相对熟悉,但对于属性填充完成后、Bean正式投入使用前的那段“初始化”旅程,往往只有一个模糊的概念——好像会调用afterPropertiesSetinit-method,好像还有一些BeanPostProcessor会插手。至于这些环节的先后顺序、源码实现细节、以及Spring设计背后的深意,却知之甚少。

​ 本文将以源码深度解析的形式,为你全景式展现Spring Bean在属性填充完成后的初始化流程。我们将从doCreateBean方法切入,逐行分析initializeBean的每一行代码,深入探讨Aware接口的回调、BeanPostProcessor的前后置处理、InitializingBeaninit-method的调用机制,并揭示AOP代理对象创建的真正时机。

二、从 doCreateBean 说起:初始化流程的入口

​ 在Spring源码中,Bean的创建核心逻辑封装在AbstractAutowireCapableBeanFactory类的doCreateBean方法中。这个方法的名字很直白——“执行创建Bean”。在经历实例化和属性填充后,初始化工作从这里拉开序幕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

// 1. 实例化Bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();

// 省略:提前暴露Bean以解决循环依赖(三级缓存处理)

// 2. 属性填充(Populate)
populateBean(beanName, mbd, instanceWrapper);

// 3. 初始化(Initialize)—— 本文的核心
exposedObject = initializeBean(beanName, exposedObject, mbd);

// 省略:注册销毁方法等
return exposedObject;
}

​ 从这段代码可以清晰地看到,initializeBean方法是属性填充后的下一站。这个方法接收三个参数:beanName、已经填充好属性的Bean实例(exposedObject),以及合并后的Bean定义(RootBeanDefinition)。返回值exposedObject可能是原始的Bean,也可能是经过BeanPostProcessor包装后的对象(比如AOP代理对象)

三、initializeBean 方法概览:初始化的四部曲

我们直接进入initializeBean方法的源码,看看Spring到底在这个方法里做了什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

// 第一步:激活Aware方法
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
} else {
invokeAwareMethods(beanName, bean);
}

// 第二步:初始化前置处理(BeanPostProcessor的前置方法)
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

// 第三步:执行初始化方法(InitializingBean + init-method)
try {
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}

// 第四步:初始化后置处理(BeanPostProcessor的后置方法)
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

这个方法的结构异常清晰,堪称模板方法模式的典范。我们可以将初始化流程概括为四部曲

  1. Aware接口回调:如果Bean实现了特定的Aware接口,Spring将注入相应的依赖。
  2. BeanPostProcessor前置处理:调用所有BeanPostProcessorpostProcessBeforeInitialization方法。
  3. 执行初始化方法:调用InitializingBeanafterPropertiesSet方法和自定义的init-method
  4. BeanPostProcessor后置处理:调用所有BeanPostProcessorpostProcessAfterInitialization方法。

下面,我们将逐一深入这四步的源码实现。

四、第一步:Aware 接口回调——让Bean感知容器

4.1 invokeAwareMethods 源码解析

先看第一步的invokeAwareMethods方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// AbstractAutowireCapableBeanFactory.java
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}

这段代码逻辑非常简单直接:检查Bean是否实现了特定的Aware接口,如果实现了,就调用对应的setter方法注入相关对象。

这里Spring只处理了三种Aware接口:

  • BeanNameAware:注入当前Bean在容器中的名称。
  • BeanClassLoaderAware:注入加载当前Bean的ClassLoader。
  • BeanFactoryAware:注入当前Bean所属的BeanFactory(即容器本身)。

4.2 为什么只有这三种Aware?

细心的读者可能会问:ApplicationContextAware在哪里?EnvironmentAware在哪里?

答案是:此时BeanFactory还不具备处理这些Aware的能力BeanNameAwareBeanClassLoaderAwareBeanFactoryAware是与BeanFactory平级的接口,它们的依赖可以直接从当前的BeanFactory中获取。而ApplicationContextAware等更高级的Aware接口,需要依赖ApplicationContext,这超出了BeanFactory的职责范围。

那么,ApplicationContextAware是在哪里被调用的呢?答案是**通过BeanPostProcessor**。Spring提供了一个ApplicationContextAwareProcessor,它是一个BeanPostProcessor,在初始化前置处理阶段(postProcessBeforeInitialization)调用ApplicationContextAwareEnvironmentAware等接口的方法。这完美诠释了为什么ApplicationContextAware等接口的回调时机在invokeAwareMethods之后。

五、第二步:初始化前置处理——BeanPostProcessor 的舞台

完成Aware回调后,进入初始化前置处理阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// AbstractAutowireCapableBeanFactory.java
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {

Object result = existingBean;
// 遍历所有的BeanPostProcessor
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 调用前置处理方法
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

这个方法遍历容器中注册的所有BeanPostProcessor,逐个调用其postProcessBeforeInitialization方法。任何一个处理器返回null,都会中断后续处理器的执行

5.1 典型应用场景

在这个阶段,Spring内置的一些BeanPostProcessor会执行特定的操作:

  • ApplicationContextAwareProcessor:回调ApplicationContextAwareEnvironmentAware等接口。
  • InitDestroyAnnotationBeanPostProcessor:负责处理@PostConstruct注解的方法。实际上,@PostConstruct标注的方法就是在这个阶段被调用的。
  • CommonAnnotationBeanPostProcessor:除了处理@Resource,也负责@PostConstruct@PreDestroy

这里有一个重要的知识点:**@PostConstruct的执行时机早于InitializingBeanafterPropertiesSetinit-method**,因为它是在初始化前置处理阶段被调用的。

六、第三步:执行初始化方法——afterPropertiesSet 与 init-method

初始化前置处理完成后,Spring开始执行Bean自身的初始化逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// AbstractAutowireCapableBeanFactory.java
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {

// 首先检查是否实现了 InitializingBean 接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
// 直接调用 afterPropertiesSet() 方法
((InitializingBean) bean).afterPropertiesSet();
}
}

// 然后检查是否指定了自定义的 init-method
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 通过反射调用自定义的初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

6.1 执行顺序的深意

这段代码清晰地表明:

  1. **先执行afterPropertiesSet**:如果Bean实现了InitializingBean接口,Spring会先调用其afterPropertiesSet方法。
  2. **后执行init-method**:如果配置了自定义的初始化方法(通过@Bean(initMethod="...")或XML的init-method属性),Spring随后会通过反射调用该方法。

为什么是这个顺序?这体现了Spring的设计哲学:框架级别的接口优先于用户配置InitializingBean是Spring框架提供的接口,而init-method是用户自定义的方法。框架认为自己的回调应该先执行,为用户方法做准备,用户方法可以在框架准备好的基础上进行后续操作。

6.2 两种方式的比较

特性 InitializingBean init-method
耦合度 与Spring API耦合 无侵入,POJO即可
实现方式 实现接口,重写方法 通过反射调用任意方法
执行时机 较早,在init-method之前 较晚,在afterPropertiesSet之后
适用场景 需要明确感知Spring容器的场景 希望与Spring解耦的场景

Spring官方推荐使用init-method(或@PostConstruct),因为它们不会让你的代码与Spring API绑定。

七、第四步:初始化后置处理——AOP的诞生之地

初始化方法的执行并非终点,Spring还有最后一项工作:初始化后置处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// AbstractAutowireCapableBeanFactory.java
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {

Object result = existingBean;
// 遍历所有的BeanPostProcessor
for (BeanPostProcessor processor : getBeanPostProcessors()) {
// 调用后置处理方法
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}

6.1 AOP代理创建的真正时机

这里要揭示一个至关重要的结论:AOP代理对象正是在这个阶段创建的

让我们通过一个简单的AOP配置来验证。假设我们有一个UserService类,需要对其方法进行切面增强。通过debug跟踪可以发现,在initializeBean方法调用前,exposedObject是原始的UserService对象;经过applyBeanPostProcessorsAfterInitialization处理后,exposedObject变成了一个代理对象(可能是JDK动态代理或CGLIB代理)。

这个代理是谁创建的?答案是**AbstractAutoProxyCreator**(通常的具体实现是AnnotationAwareAspectJAutoProxyCreator),它实现了BeanPostProcessor接口。在postProcessAfterInitialization方法中,它会检查当前Bean是否匹配任何切面(Advisor),如果匹配,就创建代理对象并返回。

1
2
3
4
5
6
7
8
9
10
11
// AbstractAutoProxyCreator.java(简化版)
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 如果需要被代理,这里会返回代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

6.2 为什么AOP代理在初始化后创建?

这个问题涉及Spring解决循环依赖的设计。如果AOP代理在实例化后立即创建,那么在处理循环依赖时,三级缓存中存储的将是代理对象的早期引用,这会导致一些问题。Spring的巧妙之处在于:提前暴露原始Bean的早期引用,而在初始化完成后才创建代理。这样既保证了循环依赖的顺利解决,又确保了最终暴露给外部的是增强后的代理对象。

八、完整的初始化流程图解

我将上述流程整合为以下时序图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
doCreateBean

createBeanInstance (实例化)

populateBean (属性填充)

initializeBean ──────────────────────────────────────────────────┐
↓ │
invokeAwareMethods │
├── BeanNameAware.setBeanName() │
├── BeanClassLoaderAware.setBeanClassLoader() │
└── BeanFactoryAware.setBeanFactory() │
↓ │
applyBeanPostProcessorsBeforeInitialization │
├── ApplicationContextAwareProcessor (回调ApplicationContext等)│
├── InitDestroyAnnotationBeanPostProcessor (调用@PostConstruct)│
└── 其他自定义BeanPostProcessor │
↓ │
invokeInitMethods │
├── InitializingBean.afterPropertiesSet() │
└── 自定义init-method (通过反射) │
↓ │
applyBeanPostProcessorsAfterInitialization │
├── AbstractAutoProxyCreator (创建AOP代理) │
└── 其他自定义BeanPostProcessor │
↓ │
返回最终的Bean (可能是原始Bean或代理对象) ←────────────────────────┘

注册DisposableBean (用于销毁阶段)

九、扩展机制:BeanPostProcessor 的魔力

通过上述分析,相信读者已经意识到BeanPostProcessor在整个初始化流程中的核心地位。它不仅是AOP的实现基础,也是Spring众多扩展功能的入口。

8.1 BeanPostProcessor 接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface BeanPostProcessor {
// 初始化前调用
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

// 初始化后调用
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

8.2 实战:自定义BeanPostProcessor

通过实现BeanPostProcessor,开发者可以干预每个Bean的初始化过程。以下是一个简单的示例,用于记录Bean初始化的耗时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class TimeCostBeanPostProcessor implements BeanPostProcessor {

private final Map<String, Long> startTimeMap = new ConcurrentHashMap<>();

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
startTimeMap.put(beanName, System.currentTimeMillis());
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Long start = startTimeMap.remove(beanName);
if (start != null) {
long cost = System.currentTimeMillis() - start;
System.out.println("Bean " + beanName + " 初始化耗时: " + cost + "ms");
}
return bean;
}
}

这个处理器会在每个Bean初始化前记录开始时间,初始化后计算耗时并输出。这种无侵入的增强方式正是BeanPostProcessor的魅力所在。

十、总结与思考

至此,我们从源码层面完整剖析了Spring Bean属性填充后的初始化流程。让我们回顾一下关键要点:

  1. 初始化流程包含四步:Aware回调 → 前置处理 → 初始化方法 → 后置处理,每一环都有其独特的设计意图。
  2. Aware接口分两组BeanNameAware等基础Aware在invokeAwareMethods中处理;ApplicationContextAware等高级Aware通过ApplicationContextAwareProcessor处理。
  3. 初始化方法有三类@PostConstruct(前置处理中)、afterPropertiesSet(InitializingBean接口)、init-method(自定义方法),它们的执行顺序严格固定。
  4. AOP代理创建于后置处理AbstractAutoProxyCreatorpostProcessAfterInitialization中生成代理对象,这是AOP与IoC整合的关键。
  5. BeanPostProcessor是扩展之王:几乎所有的Spring扩展功能(AOP、注解处理、属性解析等)都依赖于这个核心接口

深入理解Spring Bean初始化流程
https://johnjoyjzw.github.io/2025/01/01/深入理解Spring Bean初始化流程/
Author
JiangZW
Posted on
January 1, 2025
Licensed under