当前位置: 首页 > news >正文

深入理解 Spring ApplicationContext 的 refresh() 方法:容器启动的核心流程

文章目录

  • 深入理解 Spring ApplicationContext 的 refresh() 方法:容器启动的核心流程
    • 一、refresh() 方法概览
    • 二、关键步骤详解与调试要点
      • 步骤 1:prepareRefresh() —— 初始化上下文环境
      • 步骤 2:obtainFreshBeanFactory() —— 创建 BeanFactory
      • 步骤 3:prepareBeanFactory(beanFactory) —— 配置标准特性
      • 步骤 5:invokeBeanFactoryPostProcessors() —— 执行 BFPP
      • 步骤 6:registerBeanPostProcessors() —— 注册 BPP
      • 步骤 11:finishBeanFactoryInitialization() —— 初始化单例 Bean
      • 步骤 12:finishRefresh() —— 发布事件
    • 三、代码示例:自定义扩展 refresh 流程
      • 场景:在 Web 容器启动后初始化缓存
    • 四、常见问题与解决方案
      • ❌ 问题 1:Bean 循环依赖导致启动失败
      • ❌ 问题 2:自定义 BFPP 未按预期执行
      • ❌ 问题 3:refresh() 被多次调用导致重复初始化
    • 五、最佳实践与注意事项
      • ✅ 推荐做法
      • ⚠️ 注意事项
    • 六、总结
    • 💡上周热门博文

深入理解 Spring ApplicationContext 的 refresh() 方法:容器启动的核心流程

在 Spring 应用中,ApplicationContext.refresh()是整个 IoC 容器初始化与启动的“总开关”。无论是基于 XML 的ClassPathXmlApplicationContext,还是注解驱动的AnnotationConfigApplicationContext,亦或是 Spring Boot 中的SpringApplication.run(),最终都会调用refresh()方法来完成上下文的构建。

本文将系统剖析refresh()方法的 12 个关键步骤,结合源码、典型问题与调试技巧,帮助开发者深入理解 Spring 容器的启动机制。


一、refresh() 方法概览

refresh()定义于ConfigurableApplicationContext接口,在AbstractApplicationContext中实现。其整体流程如下(简化版):

@Overridepublicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this.startupShutdownMonitor){// 1. 准备刷新prepareRefresh();// 2. 创建并初始化 BeanFactoryConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();// 3. 配置 BeanFactory 的标准特性prepareBeanFactory(beanFactory);try{// 4. 允许子类进一步定制 BeanFactorypostProcessBeanFactory(beanFactory);// 5. 调用所有 BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);// 6. 注册所有 BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 7. 初始化消息源(国际化)initMessageSource();// 8. 初始化事件广播器initApplicationEventMulticaster();// 9. 留给子类扩展(如 Web 容器初始化 ServletContext)onRefresh();// 10. 注册监听器registerListeners();// 11. 实例化所有非懒加载的单例 BeanfinishBeanFactoryInitialization(beanFactory);// 12. 完成刷新,发布 ContextRefreshedEventfinishRefresh();}catch(BeansExceptionex){destroyBeans();// 失败时销毁已创建的 BeancancelRefresh(ex);throwex;}}}

📌设计原则

  • 模板方法模式refresh()是骨架,关键步骤由子类或回调扩展;
  • 失败安全:任何阶段异常都会触发destroyBeans(),避免资源泄漏。

二、关键步骤详解与调试要点

步骤 1:prepareRefresh() —— 初始化上下文环境

  • 设置启动时间戳;
  • 标记active = true
  • 初始化属性源(如environment)。

调试建议
在此处可检查Environment是否包含预期的配置文件(如application-dev.yml)。


步骤 2:obtainFreshBeanFactory() —— 创建 BeanFactory

  • 调用refreshBeanFactory()(由子类实现,如GenericApplicationContext直接返回已有DefaultListableBeanFactory);
  • 返回一个“新鲜”的BeanFactory

💡注意
FileSystemXmlApplicationContext会在此阶段加载 XML 文件并注册 BeanDefinition


步骤 3:prepareBeanFactory(beanFactory) —— 配置标准特性

BeanFactory注册关键组件:

  • ClassLoader
  • BeanExpressionResolver(支持 SpEL);
  • PropertyEditorRegistrar
  • 核心 Aware 接口支持(如ApplicationContextAwareProcessor);
  • 忽略某些接口的自动装配(如BeanFactoryAware)。
beanFactory.addBeanPostProcessor(newApplicationContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

影响
此步骤决定了后续@Autowired ApplicationContext是否生效。


步骤 5:invokeBeanFactoryPostProcessors() —— 执行 BFPP

这是配置类解析、@ConfigurationProperties 绑定、占位符替换的关键阶段。

  • ConfigurationClassPostProcessor:解析@Configuration@ComponentScan@Import
  • PropertySourcesPlaceholderConfigurer:替换${...}占位符;
  • 自定义BeanDefinitionRegistryPostProcessor优先执行。

⚠️常见问题
若自定义BFPP未被调用,检查是否正确注册为@Component或通过@Import引入。


步骤 6:registerBeanPostProcessors() —— 注册 BPP

按优先级排序并注册所有BeanPostProcessor,包括:

  • AutowiredAnnotationBeanPostProcessor(处理@Autowired);
  • CommonAnnotationBeanPostProcessor(处理@PostConstruct);
  • AnnotationAwareAspectJAutoProxyCreator(AOP 代理入口)。

🔍重要顺序
AOP 代理器必须在普通 BPP 之后注册,否则无法拦截初始化后的 Bean。


步骤 11:finishBeanFactoryInitialization() —— 初始化单例 Bean

  • 冻结 BeanDefinition(禁止后续修改);
  • 调用beanFactory.preInstantiateSingletons()
  • 按依赖顺序实例化所有非懒加载的 singleton Bean

性能瓶颈点
大型应用在此阶段耗时最长,可通过@Lazy延迟初始化非关键 Bean。


步骤 12:finishRefresh() —— 发布事件

  • 启动LifecycleBean(如SmartLifecycle.start());
  • 发布ContextRefreshedEvent
  • 注册 MBean(JMX 支持)。
// 监听上下文刷新完成@ComponentpublicclassStartupListener{@EventListenerpublicvoidonContextReady(ContextRefreshedEventevent){System.out.println("ApplicationContext is ready!");}}

三、代码示例:自定义扩展 refresh 流程

场景:在 Web 容器启动后初始化缓存

publicclassCustomWebApplicationContextextendsAnnotationConfigServletWebServerApplicationContext{@OverrideprotectedvoidonRefresh(){super.onRefresh();// 必须调用父类// 此时 ServletContext 已可用initCacheManager();}privatevoidinitCacheManager(){// 从 ApplicationContext 获取 Bean 并初始化CacheServicecache=getBean(CacheService.class);cache.warmUp();}}

适用场景
onRefresh()是 Web 应用集成 Servlet 容器的标准扩展点。


四、常见问题与解决方案

❌ 问题 1:Bean 循环依赖导致启动失败

现象

BeanCurrentlyInCreationException: ... requested by circular reference

原因

  • 构造器注入的循环依赖(Spring 无法解决);
  • 原型(prototype)Bean 的循环依赖。

解决方案

  • 改用 setter 或 field 注入;
  • 使用@Lazy打破依赖链;
  • 重构设计,消除强耦合。
@ServicepublicclassA{@Lazy@AutowiredprivateBb;// 延迟初始化 B}

❌ 问题 2:自定义 BFPP 未按预期执行

原因

  • BFPP 本身依赖了尚未注册的 Bean;
  • 未实现PriorityOrdered导致执行顺序靠后。

修复方式

@ComponentpublicclassMyBFPPimplementsBeanDefinitionRegistryPostProcessor,PriorityOrdered{@OverridepublicintgetOrder(){returnOrdered.HIGHEST_PRECEDENCE;// 最高优先级}@OverridepublicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry){// 在其他 BFPP 之前执行}}

❌ 问题 3:refresh() 被多次调用导致重复初始化

风险
refresh()不是幂等的,多次调用会抛出IllegalStateException

安全做法

  • 确保每个ApplicationContext只调用一次refresh()
  • 使用ConfigurableApplicationContext.isRunning()判断状态;
  • 在测试中使用@DirtiesContext控制上下文生命周期。

五、最佳实践与注意事项

✅ 推荐做法

  1. 避免在 BFPP/BPP 中执行耗时操作,会阻塞容器启动;
  2. 关键初始化逻辑放在ContextRefreshedEvent监听器中,此时所有 Bean 已就绪;
  3. 使用@DependsOn显式控制 Bean 初始化顺序,而非依赖隐式顺序;
  4. 生产环境开启spring.main.lazy-initialization=true(Spring Boot 2.2+)加速启动。

⚠️ 注意事项

  • refresh()是同步且阻塞的,不要在 Web 请求线程中调用;
  • 子类重写onRefresh()必须调用super.onRefresh()
  • 容器关闭时需调用close(),否则单例 Bean 的@PreDestroy不会执行。

六、总结

ApplicationContext.refresh()是 Spring 容器的“生命之源”,它以清晰的阶段划分和扩展点设计,支撑了从简单配置到复杂企业应用的初始化需求。理解其 12 个步骤的职责与交互,不仅能帮助我们高效排查启动问题,也为自定义容器行为(如多租户、动态模块加载)提供了坚实基础。

建议结合调试(在refresh()各步骤设断点)与日志(开启logging.level.org.springframework=DEBUG),逐步掌握这一核心机制。在深入原理之后,你将能更自信地驾驭 Spring 应用的生命周期。


💡上周热门博文

  • Spring 事务源码导读:从 @Transactional 到底层数据库提交的完整流程
  • Spring 中不同 Scope 的 Bean 创建机制详解
  • Spring XML 配置中<import>标签的解析机制与最佳实践
  • Spring XML 解析中的 Document 加载与 EntityResolver 机制详解
http://www.jsqmd.com/news/427320/

相关文章:

  • Neeshck-Z-lmage_LYX_v2快速部署教程:5分钟搭建国产AI绘画工具
  • Skill语言学习_3
  • 2026年深圳正宗云南菜/云南野生菌火锅评测榜单:锦上花鲜菌馆特色云南菜 - 深圳昊客网络
  • PP-DocLayoutV3多场景应用:从合同审核到教材扫描,统一版面分析能力底座
  • 2026年3月氦质谱检漏仪厂家推荐榜,彰显国产技术实力 - 品牌鉴赏师
  • 摆脱论文困扰!10个AI论文写作软件测评:MBA毕业论文+开题报告高效助手推荐
  • 2026年3月mvr蒸发器厂家权威推荐,高性能,稳定性强的行业优选 - 品牌鉴赏师
  • 2026年2月冷却塔厂家哪家好:开式闭式设备甄选指南 - 深度智识库
  • 乙巳马年皇城大门春联生成终端:5分钟打造专属AI春联,新年仪式感拉满
  • 收藏!小白程序员必备:如何用AI路由优化大模型使用,省钱又高效
  • 2026年3月箱式变电站厂家推荐,高性能与可靠性兼具优质品牌 - 品牌鉴赏师
  • YOLOv12人工智能教学案例:从理论到实践的完整课程设计
  • FLUX小红书极致真实V2图像生成工具VLOOKUP数据匹配应用
  • 最全攻略:山东一卡通回收流程和注意事项 - 团团收购物卡回收
  • Qwen3-ASR-0.6B生产环境:supervisor进程守护+异常自动重启策略
  • AIGlasses OS Pro 安装包制作:为内部团队封装一键安装部署工具
  • 解锁论文写作新境界:书匠策AI,你的期刊论文全能助手
  • CPU基础
  • Cosmos-Reason1-7B与MySQL联动开发:构建智能数据库查询与分析系统
  • 探秘书匠策AI:解锁期刊论文写作的智慧钥匙
  • 2026年3月变压器厂家推荐,全铜芯变压器实力榜单 - 品牌鉴赏师
  • CI/CD 攻防:从 GitHub Actions 到 GitLab CI 的供应链漏洞挖掘实战
  • Nanbeige 4.1-3B极简WebUI实测:像玩游戏一样与AI对话
  • 实测阿里Z-Image-GGUF:低显存友好,消费级显卡也能跑高清生图
  • 2026年2月亲测:合肥高性价比门窗品牌推荐 - 界川
  • YOLOv8改进点(不需要自己改)深度学习框架YOLOv8改进点(无需修改)复制yaml模型文件绝对路径即可进行训练 适用于目标检测,语义_分割分割,图像分类等
  • DAMOYOLO-S实战体验:上传图片秒出结果,小白也能玩转目标检测
  • 解锁学术新秘籍:书匠策AI,期刊论文创作的智慧导航者
  • 网络编程5
  • 推荐下江苏专业做电磁仿真服务的公司|2026全新原创选型指南 - 冠顶工业设备