温故而知新,忆 Spring Bean 加载全流程

一、Bean 加载只干三件事

其实 Bean 的加载只做了三件事,解析 → 注册 → 实例化;

  1. 解析配置: 无论 XML、注解还是 JavaConfig,本质都是把 <bean>@Component 之类的信息解析成 BeanDefinition
  2. 注册元数据: BeanDefinition 统一塞进 BeanDefinitionRegistry(默认实现是 DefaultListableBeanFactory)。
  3. 实例化 & 依赖注入: 真正 new 出对象,填充属性,执行各种回调,最后放进单例池。

二、refresh() 时序图

refresh() 几乎囊括了 IoC 全部门道,后面几段都从这里往下钻。接下来,我们通俗的解释一下,究竟都做了什么。

  1. refresh() 负责容器生命周期的「自举」。
  2. prepareRefresh() 校验环境、初始化属性占位符。
  3. obtainFreshBeanFactory() 创建或刷新 DefaultListableBeanFactory,确保是干净的工厂实例。
  4. prepareBeanFactory() 给工厂塞基础组件(如 Environment、SystemProperties 等)、注册内置 BeanPostProcessor。
  5. postProcessBeanFactory() 是模板方法,留给 AbstractApplicationContext 的子类做额外处理。
  6. invokeBeanFactoryPostProcessors() → 修改/新增 BeanDefinition。
  7. registerBeanPostProcessors() → 把实例阶段的增强器排好序。
  8. finishBeanFactoryInitialization() → 真正实例化非懒加载单例。
  9. finishRefresh() → 发布 ContextRefreshedEvent 等收尾动作。

想象你在租一间毛坯办公室:prepareRefresh() 像是验收水电、检查网络是否通。obtainFreshBeanFactory() 把空房打扫干净准备装修。prepareBeanFactory() 先把公共设施(饮水机、打印机)搬进来。postProcessBeanFactory() 如果你有特殊需求(比如隔出会议室),可以在这一步动手。接着两拨装修队依次进场:第一拨改图纸(BeanFactoryPostProcessor),第二拨负责软装(BeanPostProcessor)。finishBeanFactoryInitialization() 就是把桌椅板凳全部装配好。最后 finishRefresh() 剪彩开业,通知大家可以正常办公了。

三、BeanDefinition 从哪来?

3.1 XML 解析

ini 复制代码
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("beans.xml");

内部委托 BeanDefinitionParserDelegate,把 <bean><context:component-scan> 等标签转成 BeanDefinition

3.2 注解扫描

AnnotationConfigApplicationContext 启动后会触发 ConfigurationClassPostProcessor,扫描 @Configuration/@Component,同样得到 BeanDefinition

结论:资源形式千差万别,落地结果都在 BeanDefinitionRegistry 的那张 Map 里。

四、元数据阶段的扩展:BeanFactoryPostProcessor

调用栈:refresh()invokeBeanFactoryPostProcessors()PostProcessorRegistrationDelegate

执行顺序按接口排序:PriorityOrdered → Ordered → 普通

典型场景:

  • MyBatis 的 MapperScannerConfigurer
  • Spring Boot 的 ConfigurationClassPostProcessor(解析 @EnableAutoConfiguration

五、实例阶段的扩展:BeanPostProcessor

注册发生在 registerBeanPostProcessors(),之后每个 Bean 创建都会走一遍列表里的 Processor。

常见实现:

  • AutowiredAnnotationBeanPostProcessor:处理 @Autowired@Value
  • AopProxyCreator:生成 AOP 代理
  • ApplicationContextAwareProcessor:注入各种 xxxAware

六、doCreateBean 内幕

  1. A:某些 InstantiationAwareBeanPostProcessor(如 AOP)可在真正 new 对象前直接返回代理,跳过后续流程。
  2. B createBeanInstance:挑构造器、反射或 CGLIB 生成实例。
  3. C populateBean:完成依赖注入、@Autowired、字段填充。
  4. D initializeBean:执行各种回调:Aware、@PostConstruct、BeanPostProcessor 的 before/after。
  5. E registerDisposableBeanIfNecessary:如是单例且实现了 DisposableBean,注册销毁回调。

我们用做菜来举例,A 厨子有时会发现食材已经是现成半成品(代理对象),就不用再切洗。B 选刀具 + 切配 = 创建实例。C 加盐、味精、下配料 = 属性注入。D 开火翻炒到收汁 = 初始化阶段。E 把锅盖好、记住关火步骤 = 注册销毁回调。最终一道成品菜端上桌(返回 Bean)。

源码(精简):

scss 复制代码
protected Object doCreateBean(String name, RootBeanDefinition mbd, Object[] args) {
    BeanWrapper bw = createBeanInstance(name, mbd, args);  // 构造
    populateBean(name, mbd, bw);                           // 属性注入
    return initializeBean(name, bw.getWrappedInstance(), mbd); // 回调 + BPP
}

七、生命周期回调速查

  1. 构造函数 / 工厂方法
  2. 依赖注入(@Autowired
  3. BeanNameAware / BeanFactoryAware / EnvironmentAware ...
  4. BeanPostProcessor#postProcessBeforeInitialization
  5. InitializingBean#afterPropertiesSet@PostConstruct
  6. BeanPostProcessor#postProcessAfterInitialization
  7. 容器关闭:DisposableBean#destroy@PreDestroyDestructionAwareBeanPostProcessor

八、三级缓存与循环依赖

  • singletonFactories 保存的是 ObjectFactory,调用它可获得「只执行构造、不跑初始化」的早期对象。
  • earlySingletonObjects 放已经拿出来用过的早期对象。
  • singletonObjects 只有完成全部生命周期的 Bean 才能晋级。
  • 当 Bean A 构造完,但初始化前依赖了 Bean B,容器会把 A 的 ObjectFactory 暂存到三级缓存,让 B 可以先拿到「半成品 A」,避免死循环。

把三级缓存想成奶茶店的 3 个货架:三级货架 是奶茶原液,你自己加冰加料才喝得下。二级货架 是加好冰却没封口的杯子,员工可以先尝一口。一级货架 才是贴好封膜、可以递给顾客的成品。有顾客点了「连锁奶茶 + 甜品组合」导致两边互相依赖时,店员就会把「原液」先放到三级货架,另一边可以先用,等两杯都弄好再一起封口。

九、依赖解析小细节

调用链:resolveDependency()doResolveDependency()

关键点:

  • 同类型候选 Bean 名先拿出来
  • 如果参数带 @Qualifier,精确匹配
  • 对集合 / 数组参数按顺序注入
  • Optional<T>ObjectProvider<T> 也是这里处理

十、Spring Boot 与自动装配

Boot 在 SpringApplication#refreshContext() 里依旧调用 refresh(),只是多注册了若干 BeanFactoryPostProcessor

  1. ConfigurationClassPostProcessor:识别 @EnableAutoConfiguration
  2. AutoConfigurationImportSelector:从 spring.factories 里挑符合条件的配置类
  3. ConditionEvaluationReport:筛掉不满足 @Conditional 的 BeanDefinition

一句话:Boot 只是「写了很多 PostProcessor」,底层流程没变。

十一、自定义扩展示例

11.1 BeanFactoryPostProcessor 给 Mapper 加表名前缀

typescript 复制代码
@Component
public class TablePrefixProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
        for (String name : bf.getBeanDefinitionNames()) {
            if (name.endsWith("Mapper")) {
                BeanDefinition bd = bf.getBeanDefinition(name);
                bd.getConstructorArgumentValues()
                  .addIndexedArgumentValue(0, "demo_" + name);
            }
        }
    }
}

11.2 BeanPostProcessor 统计初始化耗时

typescript 复制代码
@Component
public class TimerPostProcessor implements BeanPostProcessor {
    private final Map<String, Long> ts = new ConcurrentHashMap<>();

    public Object postProcessBeforeInitialization(Object bean, String name) {
        ts.put(name, System.nanoTime());
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String name) {
        long cost = System.nanoTime() - ts.remove(name);
        System.out.printf("%s 初始化耗时 %d μs%n", name, cost / 1000);
        return bean;
    }
}

十二、知识卡片

场景 关键接口 / 类
元数据扩展 BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor
实例扩展 BeanPostProcessor, InstantiationAwareBeanPostProcessor
生命周期 InitializingBean, DisposableBean, *Aware, @PostConstruct
缓存 singletonObjects, earlySingletonObjects, singletonFactories
Boot 自动装配 ConfigurationClassPostProcessor, AutoConfigurationImportSelector

当你把 AbstractAutowireCapableBeanFactory#doCreateBean() 的调用栈捋顺,再配合两类 PostProcessor 的时机点,Spring 的「黑魔法」基本就拆完了。此后不管是写自定义 Starter,还是排查循环依赖、Bean 覆盖等疑难杂症,都能快速定位到源码里对应的阶段。

愿这份笔记能帮你把 IoC 容器的运行机理牢牢刻进肌肉记忆。

相关推荐
倔强青铜三9 分钟前
苦练Python第18天:Python异常处理锦囊
人工智能·python·面试
倔强青铜三18 分钟前
苦练Python第17天:你必须掌握的Python内置函数
人工智能·python·面试
军军君011 小时前
基于Springboot+UniApp+Ai实现模拟面试小工具四:后端项目基础框架搭建下
spring boot·spring·面试·elementui·typescript·uni-app·mybatis
why技术1 小时前
也是出息了,业务代码里面也用上算法了。
java·后端·算法
白仑色2 小时前
完整 Spring Boot + Vue 登录系统
vue.js·spring boot·后端
阳火锅3 小时前
Vue 开发者的外挂工具:配置一个 JSON,自动造出一整套页面!
javascript·vue.js·面试
倔强青铜三3 小时前
苦练Python第16天:Python模块与import魔法
人工智能·python·面试
多啦C梦a4 小时前
【适合小白篇】什么是 SPA?前端路由到底在路由个啥?我来给你聊透!
前端·javascript·架构
ZhangApple4 小时前
微信自动化工具:让自己的微信变成智能机器人!
前端·后端
Codebee4 小时前
OneCode 3.0: 注解驱动的Spring生态增强方案
后端·设计模式·架构