【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 的设计思想。

相关推荐
不愿是过客20 小时前
java实战干货——长方法深递归
java
小北方城市网21 小时前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
六义义1 天前
java基础十二
java·数据结构·算法
毕设源码-钟学长1 天前
【开题答辩全过程】以 基于SpringBoot的智能书城推荐系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
笨手笨脚の1 天前
深入理解 Java 虚拟机-03 垃圾收集
java·jvm·垃圾回收·标记清除·标记复制·标记整理
莫问前路漫漫1 天前
WinMerge v2.16.41 中文绿色版深度解析:文件对比与合并的全能工具
java·开发语言·python·jdk·ai编程
雨中飘荡的记忆1 天前
Spring AI Gateway:从入门到实战,打造智能AI服务网关
人工智能·spring·gateway
九皇叔叔1 天前
【03】SpringBoot3 MybatisPlus BaseMapper 源码分析
java·开发语言·mybatis·mybatis plus
挖矿大亨1 天前
c++中的函数模版
java·c++·算法
a程序小傲1 天前
得物Java面试被问:RocketMQ的消息轨迹追踪实现
java·linux·spring·面试·职场和发展·rocketmq·java-rocketmq