Spring框架之ConfigurableApplicationContext源码学习

简单介绍

ConfigurableApplicationContext 是 Spring 的 SPI 接口 (org.springframework.context.ConfigurableApplicationContext),是可配置、可刷新、可关闭的 ApplicationContext ,SpringBoot SpringApplication.run() 返回值就是这个类型。

定位:ApplicationContext 的可修改版本,负责容器生命周期管控,面向框架启动 / 销毁代码,业务代码尽量不要直接调用它的 refresh/close。

架构设计

plaintext 复制代码
BeanFactory(基础Bean获取)
    ↑
ApplicationContext(增强:事件、国际化、资源、层级容器)
    ↑
ConfigurableApplicationContext(SPI:新增【配置能力 + 完整生命周期】)
        ├─ ConfigurableWebApplicationContext(web环境子接口)
        └─ 实现:AbstractApplicationContext(核心实现,几乎所有上下文都继承它)

接口隔离原则 ISP

  • ApplicationContext:面向业务应用层 ,只暴露查询、获取 Bean、发布事件等只读能力,隐藏容器危险操作;
  • ConfigurableApplicationContext:面向Spring 框架、启动器、中间件、测试代码 的 SPI,暴露修改配置、生命周期、内部工厂操作。

设计目的:业务代码拿ApplicationContext看不到refresh()/close()这类高危方法,避免业务随意篡改容器状态引发不可预期问题。只有强转成ConfigurableApplicationContext才能调用生命周期方法。

完整容器生命周期模型

定义标准状态流转:

未初始化 → refresh()刷新启动 → isActive=true运行态 → close()关闭 → isActive=false销毁

复制代码
new容器对象 → refresh()【核心启动】 → 容器运行 → registerShutdownHook注册钩子 → close()销毁

refresh 具备原子性:刷新失败必须销毁已经实例化的单例 Bean,不能留下半初始化容器。

扩展点预埋设计

提供addBeanFactoryPostProcessor、addApplicationListener,允许外部在容器 refresh 阶段插入自定义逻辑,支撑@Configuration解析、自动配置、事件监听。

父子容器支持

setParent() 支持层级容器,经典场景 SpringMVC:

  • Root 上下文 (父):Service、DAO;
  • Servlet 子上下文:Controller;

规则:父容器 Bean 对子容器可见,子容器 Bean 对父容器不可见;关闭子容器不会关闭父容器。

门面模式

ApplicationContext是门面,真正的 Bean 仓库是内部ConfigurableListableBeanFactory。getBeanFactory()拿到底层真实 Bean 工厂,但这是后门,有严格使用时机限制。

源码解析

java 复制代码
package org.springframework.context;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import java.io.Closeable;

/**
 * SPI接口,ApplicationContext的可配置版本
 * 代表一个具备完整生命周期、可修改配置、可刷新关闭的Spring IoC容器
 * 注意:本接口方法主要供Spring框架内部调用,业务代码谨慎使用;
 * SpringApplication.run()返回的上下文实例就是该接口实现。
 */
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

	/**
	 * 配置路径分隔符常量
	 * 在单个字符串配置多个配置文件路径时,可用这些符号分割:逗号、分号、空格、制表符、换行
	 * 例:classpath:bean1.xml,classpath:bean2.xml
	 */
	String CONFIG_LOCATION_DELIMITERS = ",; \t\n";

	/**
	 * 容器中ConversionService转换器Bean的beanName
	 * ConversionService用于Spring类型转换,比如String转Date、字符串转自定义对象
	 * 用户可以自定义id=conversionService的Bean覆盖默认转换规则
	 */
	String CONVERSION_SERVICE_BEAN_NAME = "conversionService";

	/**
	 * 类加载时织入LoadTimeWeaver的beanName
	 * 用于LTW(加载时AOP织入),在类被ClassLoader加载字节码的时候做增强
	 */
	String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";

	/**
	 * Environment环境对象在容器中的beanName
	 * Environment存放profile、配置属性源(application.yml、系统属性、环境变量)
	 */
	String ENVIRONMENT_BEAN_NAME = "environment";

	/**
	 * systemProperties bean名称,封装JVM系统属性 System.getProperties()
	 */
	String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";

	/**
	 * systemEnvironment bean名称,封装操作系统环境变量 System.getenv()
	 */
	String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";

	/**
	 * 设置当前上下文唯一ID
	 * 每个ApplicationContext实例有一个id标识,日志输出、监控会打印该id;
	 * SpringBoot会自动生成,也可以手动覆盖设置。
	 * @param id 上下文标识字符串
	 */
	void setId(String id);

	/**
	 * 设置父容器,构建父子层级IoC容器
	 * 注意:只允许在容器构造/refresh之前设置,refresh运行后不建议修改父容器;
	 * 典型场景SpringMVC:root上下文作为父,servlet上下文作为子容器。
	 * 父容器Bean对子容器可见;子Bean对父不可见。close()子容器不会关闭父容器。
	 * @param parent 父上下文
	 */
	void setParent(ApplicationContext parent);

	/**
	 * 获取可修改的Environment对象
	 * 继承自ApplicationContext,返回ConfigurableEnvironment,
	 * 可以新增配置源、激活/关闭profile,修改环境属性。
	 * @return 可配置环境对象
	 */
	@Override
	ConfigurableEnvironment getEnvironment();

	/**
	 * 替换容器的Environment环境对象
	 * ⚠️必须refresh()执行之前调用;refresh之后再修改环境会引发不可预知bug。
	 * @param environment 新的环境实例
	 */
	void setEnvironment(ConfigurableEnvironment environment);

	/**
	 * 注册BeanFactoryPostProcessor后置处理器
	 * 执行时机:refresh流程中,BeanDefinition全部加载完毕,Bean实例化之前执行;
	 * 可以修改BeanDefinition定义信息。例如ConfigurationClassPostProcessor解析@Configuration。
	 * ⚠️重要:必须refresh()调用之前添加;refresh之后再添加不会被执行。
	 * @param beanFactoryPostProcessor Bean工厂后置处理器
	 */
	void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);

	/**
	 * 注册应用事件监听器ApplicationListener
	 * 1、容器未激活(refresh未执行):注册后refresh阶段就可以收到容器生命周期事件;
	 * 2、容器已经激活运行:会直接交给当前事件广播器,立刻生效监听后续事件;
	 * 可监听事件:ContextRefreshedEvent容器刷新完成、ContextClosedEvent容器关闭事件。
	 * @param listener 事件监听器
	 */
	void addApplicationListener(ApplicationListener<?> listener);

	/**
	 * ✨【Spring IoC容器最核心启动入口】刷新容器
	 * 加载/刷新配置元数据,可以是xml、注解配置、properties配置;
	 * 完整流程:准备环境→创建BeanFactory→加载BeanDefinition→执行BFPP→注册BPP→实例化非懒加载单例Bean→发布容器就绪事件。
	 * 原子性保证:如果refresh中途抛出异常,会销毁已经创建的单例Bean;
	 * 要么全部Bean初始化成功,要么全部销毁,不会残留半初始化容器。
	 * 限制:大部分实现不支持重复多次refresh(),重复调用抛出IllegalStateException。
	 * @throws BeansException Bean解析、实例化失败抛出
	 * @throws IllegalStateException 容器状态非法,例如已经刷新过、已经关闭
	 */
	void refresh() throws BeansException, IllegalStateException;

	/**
	 * 向JVM注册关闭钩子ShutdownHook
	 * JVM正常退出时(System.exit,正常kill信号)自动执行本容器close();
	 * kill‑9强制终止进程不会执行钩子。
	 * 多次调用该方法只会注册最多一个钩子,不会重复注册。
	 * SpringBoot应用默认会调用该方法,实现优雅销毁Bean执行@PreDestroy。
	 * @see java.lang.Runtime#addShutdownHook
	 * @see #close()
	 */
	void registerShutdownHook();

	/**
	 * 关闭当前上下文,释放全部资源,销毁所有缓存的单例Bean;
	 * 执行Bean销毁回调@PreDestroy、DisposableBean;发布ContextClosedEvent事件。
	 * ⚠️不会调用父容器的close,父子容器生命周期互相独立。
	 * 幂等方法:多次调用close()不会报错,已经关闭的上下文再次调用直接忽略。
	 * 继承Closeable,支持try‑with‑resources语法自动关闭容器。
	 */
	@Override
	void close();

	/**
	 * 判断容器是否处于激活运行状态
	 * 激活条件:至少执行过一次refresh(),并且尚未调用close()关闭;
	 * 很多框架扩展代码调用getBean前会先判断isActive做状态守卫。
	 * @return true=运行中;false=未初始化或者已经关闭
	 */
	boolean isActive();

	/**
	 * 获取底层内部Bean工厂 ConfigurableListableBeanFactory,真实Bean仓库。
	 * 底层实现类一般是 DefaultListableBeanFactory。
	 *
	 * ⚠️官方重要警告:
	 * 不要拿返回的BeanFactory做后置处理!refresh完成后单例已经实例化完成,此时修改工厂来不及。
	 * 如果需要修改Bean定义,要用 BeanFactoryPostProcessor,在Bean实例化之前介入。
	 *
	 * 调用时机:只能在refresh()之后、close()之前,也就是isActive()=true期间。
	 * refresh还没调用,或者已经close,调用直接抛出IllegalStateException。
	 *
	 * @return 底层Bean工厂
	 * @throws IllegalStateException 容器没有可用内部Bean工厂(未refresh或者已关闭)
	 */
	ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

关键实现类

接口本身只是契约,逻辑全部在实现:

  1. AbstractApplicationContext:抽象模板类,实现 refresh () 完整 12 步模板方法,绝大多数上下文继承它;
  2. AnnotationConfigApplicationContext:注解独立非 web 容器;
  3. AnnotationConfigServletWebServerApplicationContext:SpringBoot Web 环境默认上下文;
  4. GenericApplicationContext:通用容器,适合动态注册 BeanDefinition,测试、中间件大量使用。

重点方法深度总结

refresh()

容器启动心脏,12 步标准 IoC 初始化 。

不支持重复刷新;失败要销毁 Bean;业务禁止手动调用。

getBeanFactory()

获取底层 Bean 仓库后门。

refresh 前 /close 后调用抛异常;不要 refresh 完成后修改 BeanDefinition。

addBeanFactoryPostProcessor

添加 Bean 工厂后置处理器 。

refresh 之后添加不会执行。

addApplicationListener

添加事件监听器 。

refresh 前注册可接收刷新事件;refresh 后注册只能监听后续新事件。

registerShutdownHook()

JVM 关闭钩子。

kill‑9 不会触发销毁;只针对正常退出。

close()

销毁容器 。

幂等;不会关闭父容器。

setParent()

设置父子容器。

refresh 后禁止修改父容器。

相关推荐
无巧不成书02181 小时前
Tomcat部署JSP文件完整指南:手动部署+IDEA工程化实现 附404访问错误全排查
java·ide·windows·tomcat·intellij-idea·js
曹牧1 小时前
Eclipse:Subclipse
java·ide·eclipse
weixin_448771722 小时前
Spring框架之ApplicationContext源码学习
java·学习·spring
程序员清风2 小时前
缓存、索引与数据结构的工程选择
数据结构·spring·缓存
白露与泡影2 小时前
Spring AI Alibaba -- 文生图、文生音及向量化
java·人工智能·spring
滕州市燕猫虎计算机科技工作室个体工商户2 小时前
Java,Java NIO,Java NIO2,Java AIO,AsynchronousServerSocketChannel
java·开发语言·nio·aio·nio2
学长毕设2 小时前
基于springboot的社会志愿者管理系统-附源码
java·spring boot·后端
木头科技2 小时前
【AI 工程化第十六篇】Spring AI 多模态 RAG 实战:图片、扫描件、表格与图文证据链
java·人工智能·spring
驭渊的小故事2 小时前
从 JVM 底层看泛型:泛型擦除、Signature 属性与 Super Type Token 模式
java·开发语言·jvm