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
相关推荐
zyplayer-doc7 小时前
VuePress类静态文档站和动态知识库怎么选:两种技术路线的适用场景
javascript·人工智能·后端·安全·智能手机
rannn_1117 小时前
【力扣hot100】链表专题|160、206、234、141、142
java·算法·leetcode·链表·面试·开发
IKUN家族9 小时前
Spring IoC&DI
java·spring·rpc
用户938515635079 小时前
从前后端分离到前端接口工程:React + MockJS + Vite 解析
前端·后端·全栈
码事漫谈9 小时前
世界由什么构成——这个问题比你想的更缠人
后端
摇滚侠10 小时前
《SpringBoot 3:入门与应用实战》第 2 章 IOC 思想与实现 阅读笔记 1
spring boot·笔记·后端
山荷枝10 小时前
03-框架--Spring
java·后端·spring
ChaHae-In10 小时前
SpringBoot统一功能处理
java·spring boot·后端
coder!mq11 小时前
说几个常见的语法糖?
java·开发语言·算法