Spring源码 第十一篇:Spring 扩展点全解析 - 从容器启动到 Bean 生命周期的完整执行时序

前言

前面 10 篇,我们拆解了 IOC、Bean 生命周期、AOP、事务、SpringMVC、Boot 自动配置、环境配置、事件、类型转换。Spring 所有高阶能力、第三方框架整合(MyBatis、Feign、Dubbo),全部依赖 Spring 预留的扩展钩子。本章内容相对枯燥,到了从入门到放弃的阶段了 O(∩_∩)O。

接下来让我开始枯燥的旅程吧!!!

一、容器启动阶段扩展(Bean 还没创建)

1. BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,因此它既是 BeanDefinitionRegistryPostProcessor 也是 BeanFactoryPostProcessor。Spring 容器的执行顺序如下:

  1. 首先执行所有实现了 PriorityOrdered 接口的 BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry() 方法。
  2. 然后执行所有实现了 Ordered 接口的。
  3. 接着执行其他所有的 BeanDefinitionRegistryPostProcessor
  4. 之后,才会执行上述所有实现了 PriorityOrderedOrdered 以及其他 BeanDefinitionRegistryPostProcessorpostProcessBeanFactory() 方法(这是来自其父接口 BeanFactoryPostProcessor 的方法)。
  5. 最后,才轮到其他只实现了 BeanFactoryPostProcessor 接口的普通后置处理器。

核心源码位置:

java 复制代码
// PostProcessorRegistrationDelegate
invokeBeanFactoryPostProcessors(beanFactory);

核心能力:

  • 拥有 BeanDefinitionRegistry 能力
  • 可以手动 registry.registerBeanDefinition()
  • 核心作用:动态扫描、动态注册 Bean 定义,不用写 @Component

经典落地案例:

  • MyBatis:MapperScannerConfigurer 扫描 Mapper 接口
  • Feign:批量注册 Feign 接口 BeanDefinition

代码示例:

java 复制代码
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 这一步可以动态注册 BeanDefinition,例如:
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyService.class);
        registry.registerBeanDefinition("myService", builder.getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 这里的执行时机晚于 postProcessBeanDefinitionRegistry,但早于普通 BeanFactoryPostProcessor
        // 注意:此时不能获取尚未完全初始化的 Bean 实例,仅能操作 BeanFactory。
    }
}

典型框架落地案例:

MyBatis‑Spring 的 MapperScannerConfigurer 就是通过实现 BeanDefinitionRegistryPostProcessor,在 postProcessBeanDefinitionRegistry() 中扫描 Mapper 接口,并动态注册 MapperFactoryBean 的 BeanDefinition,从而实现无需手动配置每个 Mapper。

⚠️ 陷阱提醒:

如果同时需要实现 BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor,建议直接实现前者(因为已继承后者),并务必实现 postProcessBeanDefinitionRegistry 方法,不要只重写 postProcessBeanFactory,否则无法执行注册阶段的逻辑。

2. BeanFactoryPostProcessor

执行时机: BeanDefinition 全部加载完成,Bean 还没有实例化

核心区别:

  • BeanFactoryPostProcessor 的主要职责是修改已注册的 BeanDefinition(比如修改属性值、作用域等)。
  • BeanDefinitionRegistryPostProcessor 则专注于新增或动态注册 BeanDefinition。

经典落地:
ConfigurationClassPostProcessor 解析 @Configuration@Bean@Import、组件扫描是 Spring 全注解开发的核心底层。

区别口诀:

  • BeanDefinitionRegistryPostProcessor 👉 注册 Bean
  • BeanFactoryPostProcessor 👉 修改 Bean 定义

二、Bean 生命周期级扩展(最核心、面试最重)

1. BeanPostProcessor

执行时机: Bean 实例化 + 属性填充完成后

两个关键方法:

  1. postProcessBeforeInitialization:初始化方法之前,在 Bean 执行了 @PostConstructafterPropertiesSet() 以及自定义 init-method 之前调用。
  2. postProcessAfterInitialization:初始化方法之后,在以上所有初始化回调全部完成之后调用。

顺序控制: BeanPostProcessor 的排序依据是 PriorityOrderedOrdered 接口,而不是 @Order 注解。@Order 注解主要影响的是组件在被注入到集合时的存储顺序,并不能用于定义 BeanPostProcessor 的执行优先级。

核心源码位置: AbstractAutowireCapableBeanFactory#initializeBean()

java 复制代码
// 前置处理
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
// 初始化方法执行
invokeInitMethods(beanName, wrappedBean, mbd);
// 后置处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

核心作用: 拦截所有 Bean,包装、替换、代理、增强。

经典落地:

  • AOP 代理创建:AnnotationAwareAspectJAutoProxyCreator
  • 依赖注入:AutowiredAnnotationBeanPostProcessor 处理 @Autowired@Value
  • 事务代理、动态代理全部依靠它
1.1 InstantiationAwareBeanPostProcessor 的隐藏陷阱

⚠️ 关键陷阱: 如果 postProcessAfterInstantiation 方法返回 false,Spring 会跳过对该 Bean 实例的属性填充(populateBean),并且后续的 InstantiationAwareBeanPostProcessor 实例也不会再被调用。但请注意,其他普通的 BeanPostProcessor(如用于 AOP 的代理创建)仍然会被正常执行。

示例代码:

java 复制代码
@Component
public class MyInstantiationProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (bean instanceof SomeSpecialBean) {
            // 对于某种特殊 Bean,我们决定手动处理属性,跳过自动填充
            return false;
        }
        return true; // 绝大多数情况应返回 true
    }
}
1.2 BeanPostProcessor 注册的危险误区

🚨 非常容易出错的注册方式:

@Configuration 类中,如果使用非静态 @Bean 方法声明一个 BeanPostProcessor,Spring 会过早地创建它依赖的其他 Bean,从而破坏正常的 Bean 实例化顺序,导致容器启动失败或循环依赖。

正确做法:

  1. 将该 @Bean 方法声明为 static,或
  2. BeanPostProcessor 实现类直接用 @Component 注解并确保组件扫描路径包含它。

错误示例:

java 复制代码
@Configuration
public class AppConfig {
    @Bean // ❌ 非静态方法,会导致 MyBeanPostProcessor 依赖的 bean 被提前创建
    public MyBeanPostProcessor myBeanPostProcessor() {
        return new MyBeanPostProcessor();
    }
}

如果用非静态方法注册它们:Spring 必须先实例化你的配置类 → 才能调用方法创建后置处理器但后置处理器是用来初始化配置类的 → 死循环 / 提前初始化 / 配置不生效

正确示例:

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    public static MyBeanPostProcessor myBeanPostProcessor() { // ✅ 静态方法
        return new MyBeanPostProcessor();
    }
}

2. InitializingBean + @PostConstruct + init-method

执行顺序: @PostConstructInitializingBean#afterPropertiesSet() → 自定义 init-method

源码位置: AbstractAutowireCapableBeanFactory#invokeInitMethods

作用: 单个 Bean 初始化业务逻辑,无全局影响。

3. DisposableBean + destroy-method

执行时机: 容器关闭、单例 Bean 销毁

源码位置: DefaultSingletonBeanRegistry#destroySingletons()

4. FactoryBean

底层接口:

java 复制代码
public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
}

作用: 用来构建复杂、重对象,屏蔽复杂创建逻辑。容器最终存的是 getObject() 返回的对象。

经典落地:

  • MyBatis:SqlSessionFactoryBean
  • 连接池、中间件复杂 Bean 构建

三、注解驱动扩展(自动配置核心)

1. ImportSelector

执行时机: 解析 @Import 时执行

分类:

  1. 普通 ImportSelector
  2. DeferredImportSelector 延迟导入(所有配置类解析完再执行)

核心落地: SpringBoot 自动配置:AutoConfigurationImportSelector 加载所有自动配置类

2. ImportBeanDefinitionRegistrar

执行时机: 配置类解析阶段

作用: 手动编码批量注册 BeanDefinition,不限制注解、不限制扫描路径。

经典落地:

  • @MapperScan
  • @EnableFeignClients
  • 所有 Enable 开头的开关注解底层基本都靠它

四、事件 & 环境扩展

1. ApplicationListener / @EventListener

源码入口: AbstractApplicationContext#publishEvent()

作用: 容器刷新、启动、关闭、自定义业务事件解耦神器

2. EnvironmentPostProcessor

执行时机: SpringBoot 最早环境准备阶段

作用: 自定义加载配置文件、扩展配置源,对应第 8 篇 Environment 体系。

五、SpringMVC 专属扩展(Web 层)

对应第 6 篇 MVC 完整流程

  1. HandlerInterceptorpreHandle / postHandle / afterCompletion
  2. HandlerMethodArgumentResolver:自定义参数解析,实现自定义注解参数
  3. HandlerMethodReturnValueHandler:统一返回值封装、全局响应处理
  4. WebMvcConfigurer:跨域、拦截器、资源映射、消息转换器配置

六、全网必背:Spring 扩展点完整执行时序

  1. EnvironmentPostProcessor 👉 环境配置早期扩展
  2. BeanDefinitionRegistryPostProcessor 👉 动态注册 Bean
  3. BeanFactoryPostProcessor 👉 修改 BeanDefinition
  4. Bean 实例化 + 依赖填充
  5. BeanPostProcessor#Before
  6. @PostConstruct
  7. InitializingBean#afterPropertiesSet
  8. 自定义 init-method
  9. BeanPostProcessor#After 👉 AOP 代理、最终 Bean 成品
  10. 容器刷新完成:ContextRefreshedEvent
  11. 容器关闭:DisposableBean / destroy

七、场景选型(工作直接对照用)

需求场景 推荐扩展点
要动态造 Bean、扫接口、中间件整合 ImportBeanDefinitionRegistrar / BeanDefinitionRegistryPostProcessor
要修改全局 Bean 定义、动态配置 BeanFactoryPostProcessor
要给所有 Bean 做代理、增强、注入 BeanPostProcessor
复杂对象创建、组装 FactoryBean
自动配置、批量导入配置类 DeferredImportSelector
解耦、异步事件、业务解耦 ApplicationListener
相关推荐
Csvn30 分钟前
📊 SQL 入门 Day 8:集合操作 — 用 SQL 做数学里的"并交差"
后端·sql
腻害兔1 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
碎光拾影2 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Miao121313 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
腻害兔3 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
码事漫谈3 小时前
告别数据孤岛与AI“水土不服”:金仓多模融合时序库如何让数据真正服务于业务
后端
IT_陈寒3 小时前
Redis的持久化配置把我坑惨了:你以为数据安全了?
前端·人工智能·后端
星栈3 小时前
Node 接口该写同步还是异步?
后端·node.js
红烧大青虫4 小时前
HarmonyOS应用开发实战:小事记 - UIAbility 的冷启动/热启动/后台启动三种场景与 launchParam 解析
后端·华为·harmonyos·鸿蒙系统
码事漫谈4 小时前
AI Token 缓存:命中省 10 倍,不命中白扔钱
后端