【Spring源码】getBean源码实战(七)——BeanPostProcessor与初始化方法

getBean源码实战(七)------BeanPostProcessor与初始化方法

代码仓库Gitee 仓库链接

本文档的所有示例代码都可以在代码仓库中找到,建议结合代码一起阅读。

1. 引言

在前面的文章中,我们深入分析了 Bean 的创建流程,从 doGetBeandoCreateBean,再到 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-methodinitMethod()

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;
}

🔍 发现:这个方法清晰地展示了初始化的四个阶段:

  1. Aware 接口调用:让 Bean 获取容器信息
  2. BeanPostProcessor 前置处理:在初始化前进行扩展处理
  3. 初始化方法调用:执行实际的初始化逻辑
  4. 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);
        }
    }
}

💡 关键理解

  1. InitializingBean 优先 :如果 Bean 实现了 InitializingBean 接口,会先调用 afterPropertiesSet() 方法
  2. init-method 其次 :然后调用 XML 配置的 init-method 方法
  3. 避免重复调用 :如果 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

配置了两个 BeanPostProcessorCustomBeanPostProcessorAnotherBeanPostProcessor

💡 关键理解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 的差异

💡 重要发现@PostConstructBeanFactory 中不会自动调用,但在 ApplicationContext 中会调用!

原因

  • @PostConstruct 的处理依赖于 CommonAnnotationBeanPostProcessor
  • XmlBeanFactory 不会自动注册 CommonAnnotationBeanPostProcessor
  • ApplicationContext 会自动注册 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
  • 具体策略CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor、自定义 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 核心要点

  1. 三种初始化方式

    • @PostConstruct:注解方式,最简洁
    • InitializingBean:接口方式,类型安全
    • init-method:配置方式,解耦
  2. 执行顺序

    复制代码
    Aware 接口 → BeanPostProcessor.before → @PostConstruct → 
    afterPropertiesSet → init-method → BeanPostProcessor.after
  3. BeanPostProcessor 机制

    • 强大的扩展点
    • 在 Bean 初始化前后进行处理
    • 支持多个 BeanPostProcessor 按顺序执行
  4. BeanFactory vs ApplicationContext

    • BeanFactory 不会自动注册 CommonAnnotationBeanPostProcessor
    • ApplicationContext 会自动注册,支持 @PostConstruct

8.2 设计思想

  • 模板方法模式:定义算法骨架,提供扩展点
  • 策略模式 :通过 BeanPostProcessor 支持不同的处理策略
  • 开闭原则:对扩展开放,对修改关闭
  • 接口隔离原则:接口设计简洁,职责单一

8.3 最佳实践

  • ✅ 推荐使用 @PostConstruct 进行初始化
  • ✅ 使用 ApplicationContext 而不是 BeanFactory
  • ✅ 通过 BeanPostProcessor 实现自定义扩展
  • ✅ 理解执行顺序,避免依赖未初始化的状态

💡 最后的话BeanPostProcessor 和初始化方法是 Spring 框架中非常重要的机制,理解它们有助于我们更好地使用 Spring,也为我们提供了强大的扩展能力。通过深入源码,我们不仅了解了实现细节,更重要的是理解了 Spring 的设计思想。

相关推荐
计算机程序设计小李同学16 小时前
基于SpringBoot的个性化穿搭推荐及交流平台
java·spring boot·后端
是一个Bug16 小时前
50道核心JVM面试题
java·开发语言·面试
用户479492835691516 小时前
同事一个比喻,让我搞懂了Docker和k8s的核心概念
前端·后端
朱朱没烦恼yeye16 小时前
java基础学习
java·python·学习
她和夏天一样热17 小时前
【观后感】Java线程池实现原理及其在美团业务中的实践
java·开发语言·jvm
郑州光合科技余经理17 小时前
技术架构:上门服务APP海外版源码部署
java·大数据·开发语言·前端·架构·uni-app·php
篱笆院的狗17 小时前
Java 中的 DelayQueue 和 ScheduledThreadPool 有什么区别?
java·开发语言
2501_9418091417 小时前
面向多活架构与数据地域隔离的互联网系统设计思考与多语言工程实现实践分享记录
java·开发语言·python
qualifying18 小时前
JavaEE——多线程(4)
java·开发语言·java-ee
better_liang18 小时前
每日Java面试场景题知识点之-DDD领域驱动设计
java·ddd·实体·领域驱动设计·架构设计·聚合根·企业级开发