深入理解Spring Bean初始化流程
一、前言
如果说Spring的IoC容器是一座精密的工厂,那么Bean的创建就是这座工厂最核心的生产流水线。在这条流水线上,一个Bean从无到有,大体经历三个关键工序:实例化(Instantiation)、属性填充(Populate)和初始化(Initialization)。
很多开发者对Bean的实例化和属性注入相对熟悉,但对于属性填充完成后、Bean正式投入使用前的那段“初始化”旅程,往往只有一个模糊的概念——好像会调用afterPropertiesSet和init-method,好像还有一些BeanPostProcessor会插手。至于这些环节的先后顺序、源码实现细节、以及Spring设计背后的深意,却知之甚少。
本文将以源码深度解析的形式,为你全景式展现Spring Bean在属性填充完成后的初始化流程。我们将从doCreateBean方法切入,逐行分析initializeBean的每一行代码,深入探讨Aware接口的回调、BeanPostProcessor的前后置处理、InitializingBean和init-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
| protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance();
populateBean(beanName, mbd, instanceWrapper);
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
| protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); }
Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); }
try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); }
if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); }
return wrappedBean; }
|
这个方法的结构异常清晰,堪称模板方法模式的典范。我们可以将初始化流程概括为四部曲:
- Aware接口回调:如果Bean实现了特定的
Aware接口,Spring将注入相应的依赖。
- BeanPostProcessor前置处理:调用所有
BeanPostProcessor的postProcessBeforeInitialization方法。
- 执行初始化方法:调用
InitializingBean的afterPropertiesSet方法和自定义的init-method。
- BeanPostProcessor后置处理:调用所有
BeanPostProcessor的postProcessAfterInitialization方法。
下面,我们将逐一深入这四步的源码实现。
四、第一步:Aware 接口回调——让Bean感知容器
4.1 invokeAwareMethods 源码解析
先看第一步的invokeAwareMethods方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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的能力。BeanNameAware、BeanClassLoaderAware和BeanFactoryAware是与BeanFactory平级的接口,它们的依赖可以直接从当前的BeanFactory中获取。而ApplicationContextAware等更高级的Aware接口,需要依赖ApplicationContext,这超出了BeanFactory的职责范围。
那么,ApplicationContextAware是在哪里被调用的呢?答案是**通过BeanPostProcessor**。Spring提供了一个ApplicationContextAwareProcessor,它是一个BeanPostProcessor,在初始化前置处理阶段(postProcessBeforeInitialization)调用ApplicationContextAware、EnvironmentAware等接口的方法。这完美诠释了为什么ApplicationContextAware等接口的回调时机在invokeAwareMethods之后。
五、第二步:初始化前置处理——BeanPostProcessor 的舞台
完成Aware回调后,进入初始化前置处理阶段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean; 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:回调
ApplicationContextAware、EnvironmentAware等接口。
- InitDestroyAnnotationBeanPostProcessor:负责处理
@PostConstruct注解的方法。实际上,@PostConstruct标注的方法就是在这个阶段被调用的。
- CommonAnnotationBeanPostProcessor:除了处理
@Resource,也负责@PostConstruct和@PreDestroy。
这里有一个重要的知识点:**@PostConstruct的执行时机早于InitializingBean的afterPropertiesSet和init-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
| protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
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 { ((InitializingBean) bean).afterPropertiesSet(); } }
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 执行顺序的深意
这段代码清晰地表明:
- **先执行
afterPropertiesSet**:如果Bean实现了InitializingBean接口,Spring会先调用其afterPropertiesSet方法。
- **后执行
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
| public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean; 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
| 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属性填充后的初始化流程。让我们回顾一下关键要点:
- 初始化流程包含四步:Aware回调 → 前置处理 → 初始化方法 → 后置处理,每一环都有其独特的设计意图。
- Aware接口分两组:
BeanNameAware等基础Aware在invokeAwareMethods中处理;ApplicationContextAware等高级Aware通过ApplicationContextAwareProcessor处理。
- 初始化方法有三类:
@PostConstruct(前置处理中)、afterPropertiesSet(InitializingBean接口)、init-method(自定义方法),它们的执行顺序严格固定。
- AOP代理创建于后置处理:
AbstractAutoProxyCreator在postProcessAfterInitialization中生成代理对象,这是AOP与IoC整合的关键。
- BeanPostProcessor是扩展之王:几乎所有的Spring扩展功能(AOP、注解处理、属性解析等)都依赖于这个核心接口