getBean源码实战(七)------BeanPostProcessor与初始化方法
代码仓库 :Gitee 仓库链接
本文档的所有示例代码都可以在代码仓库中找到,建议结合代码一起阅读。
1. 引言
在前面的文章中,我们深入分析了 Bean 的创建流程,从 doGetBean 到 doCreateBean,再到 initializeBean。在这个过程中,我们发现了一个关键环节:Bean 的初始化。
💡 思考时刻:Bean 在创建完成后,还需要做什么?属性已经注入了,依赖已经解决了,那么接下来应该做什么?
让我们回顾一下 Bean 的完整生命周期:
1. 实例化(createBeanInstance)
2. 提前暴露(放入三级缓存)
3. 属性注入(populateBean)
4. 初始化(initializeBean)← 我们关注的重点
5. 放入一级缓存
在 initializeBean 方法中,Spring 做了很多事情,其中最重要的就是:
- 调用 Aware 接口方法
- 调用 BeanPostProcessor 的前置处理
- 调用初始化方法(@PostConstruct、InitializingBean、init-method)
- 调用 BeanPostProcessor 的后置处理
🔍 发现:这个流程看似简单,但实际上涉及了 Spring 的多个核心机制。让我们深入分析一下。
2. 初始化方法的三种方式
2.1 三种初始化方式
Spring 提供了三种方式来执行 Bean 的初始化逻辑:
| 方式 | 特点 | 使用场景 |
|---|---|---|
| @PostConstruct | 注解方式,最简洁 | 现代 Spring 开发的首选 |
| InitializingBean | 接口方式,类型安全 | 需要编译时检查的场景 |
| init-method | 配置方式,解耦 | 第三方库或需要灵活配置的场景 |
💡 关键理解:这三种方式不是互斥的,可以同时使用。Spring 会按照固定顺序依次调用它们。
2.2 示例 Bean:LifecycleService
让我们通过一个完整的示例来理解这三种初始化方式:
Bean 类 :code/spring-basic/src/main/java/com/example/beanlifecycle/LifecycleService.java
这个 Bean 同时实现了三种初始化方式:
@PostConstruct注解方法:postConstruct()InitializingBean接口方法:afterPropertiesSet()- XML 配置的
init-method:initMethod()
XML 配置 :code/spring-basic/src/main/resources/applicationContext-beanlifecycle.xml
配置了 init-method="initMethod" 和 destroy-method="destroyMethod"。
🔍 观察:这个 Bean 同时使用了三种初始化方式,让我们看看它们的执行顺序。
3. 初始化方法的调用顺序
3.1 完整的执行流程
当我们调用 getBean("lifecycleService") 时,Spring 会按照以下顺序执行:
1. 构造函数被调用
↓
2. 属性注入(setName)
↓
3. BeanPostProcessor.postProcessBeforeInitialization
↓
4. @PostConstruct 方法
↓
5. InitializingBean.afterPropertiesSet()
↓
6. init-method 方法
↓
7. BeanPostProcessor.postProcessAfterInitialization
💡 关键理解:这个顺序不是随意的,而是经过精心设计的。每个阶段都有其特定的目的。
3.2 源码分析:initializeBean 方法
让我们深入 AbstractAutowireCapableBeanFactory.initializeBean 方法,看看 Spring 是如何实现这个流程的:
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 1. 调用 Aware 接口方法
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()) {
// 2. BeanPostProcessor 前置处理
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 3. 调用初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(...);
}
if (mbd == null || !mbd.isSynthetic()) {
// 4. BeanPostProcessor 后置处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
🔍 发现:这个方法清晰地展示了初始化的四个阶段:
- Aware 接口调用:让 Bean 获取容器信息
- BeanPostProcessor 前置处理:在初始化前进行扩展处理
- 初始化方法调用:执行实际的初始化逻辑
- BeanPostProcessor 后置处理:在初始化后进行扩展处理(如 AOP 代理)
3.3 源码分析:invokeInitMethods 方法
让我们看看 invokeInitMethods 方法是如何调用三种初始化方式的:
java
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 1. 检查是否实现了 InitializingBean 接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 调用 InitializingBean.afterPropertiesSet()
((InitializingBean) bean).afterPropertiesSet();
}
// 2. 调用 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);
}
}
}
💡 关键理解:
- InitializingBean 优先 :如果 Bean 实现了
InitializingBean接口,会先调用afterPropertiesSet()方法 - init-method 其次 :然后调用 XML 配置的
init-method方法 - 避免重复调用 :如果
init-method指定的方法名是afterPropertiesSet,则不会重复调用
⚠️ 注意 :这里没有看到 @PostConstruct 的调用!这是因为 @PostConstruct 的处理是在 BeanPostProcessor 中完成的,而不是在 invokeInitMethods 中。
3.4 @PostConstruct 的处理机制
@PostConstruct 的处理依赖于 CommonAnnotationBeanPostProcessor,这是一个特殊的 BeanPostProcessor。
处理流程:
java
// CommonAnnotationBeanPostProcessor.postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 查找 @PostConstruct 注解的方法
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 调用 @PostConstruct 方法
metadata.invokeInitMethods(bean, beanName);
} catch (InvocationTargetException ex) {
throw new BeanCreationException(...);
}
return bean;
}
🔍 发现 :@PostConstruct 是在 BeanPostProcessor.postProcessBeforeInitialization 中调用的,所以它的执行顺序是:
- 在
InitializingBean.afterPropertiesSet()之前 - 在
init-method之前
💡 思考 :为什么 @PostConstruct 要在 afterPropertiesSet 之前调用?这可能是因为 @PostConstruct 是 JSR-250 标准注解,Spring 希望遵循标准规范,让标准注解优先执行。
4. BeanPostProcessor 机制
4.1 什么是 BeanPostProcessor
BeanPostProcessor 是 Spring 提供的一个强大的扩展点,允许在 Bean 初始化前后进行处理。
💡 关键理解 :BeanPostProcessor 是 Spring 实现很多高级功能的基础,比如:
- AOP 代理的创建
@PostConstruct和@PreDestroy的处理@Autowired的处理- 自定义的 Bean 增强
4.2 BeanPostProcessor 接口定义
java
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;
}
}
🔍 观察:这个接口定义非常简单,只有两个方法,但它的作用却非常强大。
4.3 自定义 BeanPostProcessor
让我们创建一个自定义的 BeanPostProcessor 来观察它的调用时机:
BeanPostProcessor 实现 :code/spring-basic/src/main/java/com/example/beanlifecycle/CustomBeanPostProcessor.java
这个 BeanPostProcessor 实现了两个方法:
postProcessBeforeInitialization:在初始化方法之前调用postProcessAfterInitialization:在初始化方法之后调用
XML 配置 :code/spring-basic/src/main/resources/applicationContext-beanlifecycle.xml
配置了两个 BeanPostProcessor:CustomBeanPostProcessor 和 AnotherBeanPostProcessor。
💡 关键理解 :BeanPostProcessor 本身也是一个 Bean,需要注册到容器中。Spring 会在所有其他 Bean 初始化时调用它。
4.4 多个 BeanPostProcessor 的执行顺序
如果有多个 BeanPostProcessor,Spring 会按照注册顺序依次执行:
执行顺序:
对于每个 Bean:
1. 所有 BeanPostProcessor 的 postProcessBeforeInitialization 依次执行
2. 初始化方法执行(@PostConstruct、afterPropertiesSet、init-method)
3. 所有 BeanPostProcessor 的 postProcessAfterInitialization 依次执行
🔍 发现 :这种设计确保了所有 BeanPostProcessor 都有机会处理每个 Bean,同时保持了执行顺序的一致性。
5. BeanFactory vs ApplicationContext 的差异
5.1 @PostConstruct 的差异
💡 重要发现 :@PostConstruct 在 BeanFactory 中不会自动调用,但在 ApplicationContext 中会调用!
原因:
@PostConstruct的处理依赖于CommonAnnotationBeanPostProcessorXmlBeanFactory不会自动注册CommonAnnotationBeanPostProcessorApplicationContext会自动注册CommonAnnotationBeanPostProcessor
5.2 验证测试
让我们通过测试来验证这个差异:
测试代码 :code/spring-basic/src/test/java/com/example/beanlifecycle/PostConstructComparisonTest.java
测试包含三个方法:
testPostConstructWithXmlBeanFactory:验证在XmlBeanFactory中@PostConstruct不会被调用testPostConstructWithApplicationContext:验证在ApplicationContext中@PostConstruct会被调用testPostConstructWithManualRegistration:验证手动注册CommonAnnotationBeanPostProcessor后可以正常调用
5.3 解决方案
如果需要在 BeanFactory 中使用 @PostConstruct,可以:
方案 1:手动注册 CommonAnnotationBeanPostProcessor
在代码中手动创建并注册 CommonAnnotationBeanPostProcessor 实例。
方案 2:在 XML 中配置
在 XML 配置文件中添加 CommonAnnotationBeanPostProcessor 的 Bean 定义。
💡 最佳实践 :除非有特殊需求,否则应该使用 ApplicationContext,它会自动处理这些细节。
6. Spring 的设计思想
6.1 模板方法模式
initializeBean 方法体现了模板方法模式:
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// 1. 固定步骤:调用 Aware 接口
invokeAwareMethods(beanName, bean);
// 2. 扩展点:BeanPostProcessor 前置处理
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 3. 固定步骤:调用初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
// 4. 扩展点:BeanPostProcessor 后置处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
💡 关键理解:模板方法模式定义了算法的骨架,但允许子类或扩展点(BeanPostProcessor)在特定步骤进行扩展。
6.2 策略模式
BeanPostProcessor 体现了策略模式:
- 策略接口 :
BeanPostProcessor - 具体策略 :
CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor、自定义BeanPostProcessor等 - 上下文:Spring 容器
🔍 发现:通过策略模式,Spring 可以在不修改核心代码的情况下,支持各种不同的处理逻辑。
6.3 开闭原则
Spring 的设计完美体现了开闭原则:
- 对扩展开放 :可以通过实现
BeanPostProcessor接口来扩展功能 - 对修改关闭:不需要修改 Spring 的核心代码
💡 思考:为什么 Spring 要提供三种初始化方式?这是为了满足不同场景的需求:
@PostConstruct:现代、简洁InitializingBean:类型安全、编译时检查init-method:解耦、灵活配置
6.4 接口隔离原则
BeanPostProcessor 接口设计体现了接口隔离原则:
java
public interface BeanPostProcessor {
// 只有两个方法,职责单一
Object postProcessBeforeInitialization(Object bean, String beanName);
Object postProcessAfterInitialization(Object bean, String beanName);
}
🔍 发现:接口设计简洁,只包含必要的方法,符合接口隔离原则。
7. 实际应用场景
7.1 AOP 代理的创建
AOP 代理的创建就是在 BeanPostProcessor.postProcessAfterInitialization 中完成的。
💡 关键理解 :AbstractAutoProxyCreator 实现了 BeanPostProcessor 接口,在 postProcessAfterInitialization 方法中检查 Bean 是否需要代理,如果需要则创建代理对象。AOP 代理是在 Bean 初始化完成后创建的,这样可以确保代理对象包含完整的 Bean 状态。
7.2 性能监控
可以通过 BeanPostProcessor 来实现性能监控:在 postProcessAfterInitialization 方法中为 Bean 创建性能监控代理,记录方法执行时间等指标。
7.3 属性验证
可以在 BeanPostProcessor 中进行属性验证:在 postProcessBeforeInitialization 方法中验证 Bean 的属性是否符合要求,如果不符合则抛出异常。
8. 总结
8.1 核心要点
-
三种初始化方式:
@PostConstruct:注解方式,最简洁InitializingBean:接口方式,类型安全init-method:配置方式,解耦
-
执行顺序:
Aware 接口 → BeanPostProcessor.before → @PostConstruct → afterPropertiesSet → init-method → BeanPostProcessor.after -
BeanPostProcessor 机制:
- 强大的扩展点
- 在 Bean 初始化前后进行处理
- 支持多个
BeanPostProcessor按顺序执行
-
BeanFactory vs ApplicationContext:
BeanFactory不会自动注册CommonAnnotationBeanPostProcessorApplicationContext会自动注册,支持@PostConstruct
8.2 设计思想
- 模板方法模式:定义算法骨架,提供扩展点
- 策略模式 :通过
BeanPostProcessor支持不同的处理策略 - 开闭原则:对扩展开放,对修改关闭
- 接口隔离原则:接口设计简洁,职责单一
8.3 最佳实践
- ✅ 推荐使用
@PostConstruct进行初始化 - ✅ 使用
ApplicationContext而不是BeanFactory - ✅ 通过
BeanPostProcessor实现自定义扩展 - ✅ 理解执行顺序,避免依赖未初始化的状态
💡 最后的话 :BeanPostProcessor 和初始化方法是 Spring 框架中非常重要的机制,理解它们有助于我们更好地使用 Spring,也为我们提供了强大的扩展能力。通过深入源码,我们不仅了解了实现细节,更重要的是理解了 Spring 的设计思想。