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
相关推荐
2601_962071572 小时前
【Java报错已解决】org.springframework.beans.factory.BeanCreationException
java·开发语言
zww89491113 小时前
酒馆预约系统开发实战:从需求分析到上线全流程指南
java·eclipse
东风破_4 小时前
从跨域到 WebSocket:前端跨域方案、SSE 与双向实时通信详解
前端·后端
zww89491116 小时前
匿名树洞系统开发实战:从需求分析到部署指南
java·eclipse
行百里er6 小时前
Gateway 上别硬塞 MVC:DeferredImportSelector 看菜下锅
spring boot·后端·开源
名字还没想好☜6 小时前
Java 遍历时删元素抛 ConcurrentModificationException:fail-fast 原理与三种正确删法
后端
IT_陈寒7 小时前
React Hooks闭包陷阱差点让我加班到凌晨
前端·人工智能·后端
黑马程序员毕设7 小时前
基于Java的医院药品管理系统的优化设计与实现
java·开发语言·spring boot·小程序·架构·课程设计·毕设
木井巳7 小时前
【BFS/DFS 解决 FloodFill 算法】太平洋大西洋水流问题
java·算法·leetcode·深度优先·广度优先·宽度优先·推荐算法
码事漫谈7 小时前
本体论,彻底懂
后端