Spring容器的心脏:深度解析refresh()方法(上)

在 Spring 框架的广袤世界中,有一个方法堪称整个 IoC 容器的"创世"基石。它,就是 ApplicationContext.refresh()。无论你使用的是传统的 XML 配置,还是现代化的 Spring Boot,最终都会汇集到这个方法上,由它来点燃整个应用上下文的生命之火。这个方法看似只是一个简单的 refresh 调用,但其内部却隐藏着一个精密而复杂的启动流程。今天,我们将深入其中,一同揭开这十二个标准步骤的神秘面纱,探究 Spring 容器从初始化到就绪的完整诞生记。

准备阶段 - 环境准备与前期检查

java 复制代码
this.startupShutdownLock.lock();
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 主要步骤:
prepareRefresh();                    // 准备刷新
obtainFreshBeanFactory();           // 获取BeanFactory
prepareBeanFactory(beanFactory);    // 准备BeanFactory

首先我们来看看第一阶段准备阶段所干的事情,准备阶段主要是为BeanFactory进行环境的准备和工厂的初始化。首先获取当前的lock锁用来保证整个执行过程中的线程安全,其次获取当前执行的线程,目的是将当前启动线程进行绑定,后续容器关闭的时候需要对当前线程进行校验是否为启动线程,也就是说要保证整个容器操作的线程一致性。

这行看似简单的代码实际上是Spring容器生命周期线程安全的重要保障。它确保了:

  1. 启动和关闭操作的线程一致性

  2. 防止意外的跨线程关闭操作

  3. 为容器生命周期提供可靠的线程追踪

随后的第一个方法则是prepareRefresh

prepareRefresh

java 复制代码
protected void prepareRefresh() {
		// Switch to active.
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
		}

		// Initialize any placeholder property sources in the context environment.
		initPropertySources();

		// Validate that all properties marked as required are resolvable:
		// see ConfigurablePropertyResolver#setRequiredProperties
		getEnvironment().validateRequiredProperties();

		// Store pre-refresh ApplicationListeners...
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

我们可以看到首先设置了两个spring容器的标志位,随后initPropertySources方法它的核心作用在于初始化和管理应用运行时所依赖的环境属性源(PropertySources)。在AbstractApplicationContext中是一个空方法而是留给子类去实现这个方法。

initPropertySources

其中主要功能则是添加自定义属性源

java 复制代码
@Override
protected void initPropertySources() {
    // 获取标准环境对象
    ConfigurableEnvironment env = getEnvironment();
    
    // 创建自定义属性源(例如从数据库、远程配置中心读取)
    Map<String, Object> myProperties = new HashMap<>();
    myProperties.put("jdbc.url", "jdbc:mysql://localhost:3306/myapp");
    myProperties.put("api.key", "secret-key-123");
    
    // 将自定义属性源添加到环境的最前面(最高优先级)
    env.getPropertySources().addFirst(
        new MapPropertySource("myCustomPropertySource", myProperties)
    );
    
    System.out.println("自定义属性源已加载,JDBC URL: " + env.getProperty("jdbc.url"));
}

验证必需属性 && 属性源优先级调整

java 复制代码
@Override
protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    
    // 验证这些必需属性是否已设置,如果没有则抛出异常
    env.getRequiredProperty("database.url"); // 如果不存在,会抛出 IllegalStateException
    env.getRequiredProperty("security.oauth2.client-id");
    
    // 或者使用更灵活的方式
    if (!env.containsProperty("mandatory.config")) {
        throw new IllegalStateException("必须在启动前设置 'mandatory.config' 属性");
    }
}



@Override
protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    MutablePropertySources propertySources = env.getPropertySources();
    
    // 将系统环境变量移到系统属性之前
    PropertySource<?> systemEnv = propertySources.remove("systemEnvironment");
    if (systemEnv != null) {
        propertySources.addAfter("systemProperties", systemEnv);
    }
}

initPropertySources() 方法是 Spring 环境准备阶段的关键扩展点,主要承担三大职责:

  1. 属性源定制 - 添加自定义的属性来源

  2. 属性验证 - 确保必需配置的存在性

  3. 优先级控制 - 调整属性查找顺序

保存监听器状态并初始化早期事件收集机制

java 复制代码
		// Store pre-refresh ApplicationListeners...
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		this.earlyApplicationEvents = new LinkedHashSet<>();

在容器刷新伊始,通过保存监听器的初始快照和创建临时事件缓冲区,为Spring事件机制建立一个安全的启动状态,确保在完整的事件广播器就绪之前,所有的监听器状态和早期事件都能得到妥善管理和后续处理。

简单来说,它就像为Spring的事件系统设置了一个安全的"启动准备区":保存好监听器的初始阵容,同时准备好一个临时储物柜来存放过早产生的事件,等到事件广播器这个"舞台"搭建完成后,再让演员(监听器)就位、把暂存的道具(事件)搬上舞台正式演出。

  1. 状态保存 - 保存应用监听器的初始状态,支持容器多次刷新

  2. 事件缓冲 - 为广播器初始化前产生的事件提供临时存储

  3. 时序保障 - 解决 Spring 启动过程中的依赖时序问题

  4. 容错处理 - 确保在复杂的启动过程中事件机制仍能正常工作

维度 earlyApplicationListeners applicationListeners
生效时机 容器初始化早期(refresh() 方法初期) 容器完全初始化后(refresh() 方法完成后)
监听事件类型 早期内部事件(如环境准备、Bean 定义加载等) 常规事件(如容器刷新、关闭、自定义事件等)
注册来源 主要由 Spring 内部注册 主要由用户自定义注册(也可包含内部监听器)
容器状态依赖 不依赖容器完全初始化,可在早期阶段工作 依赖容器完成初始化,确保 Bean 等资源可用

总结

因此prepareRefresh主要作用是准备容器刷新的前期工作,包括初始化上下文环境、验证必要的系统属性是否存在、初始化早期事件监听器集合(earlyApplicationListeners)并保存容器启动时的活跃标志,为后续的 Bean 定义加载、容器初始化等核心流程奠定基础,确保容器能在一个就绪的状态下开始刷新操作。

obtainFreshBeanFactory

obtainFreshBeanFactory() 方法是 Spring 容器 refresh() 流程中的关键步骤,主要作用是获取一个 "新鲜" 的(即最新状态的)BeanFactory

java 复制代码
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();
		return getBeanFactory();
	}

而我们看到整个方法实际上只执行了两个业务逻辑刷新BeanFactory和获取BeanFactory

java 复制代码
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

在这个refreshBeanFactory方法中首先则是判断当前容器是否含有BeanFactory,如果有则会对其进行销毁和清除,然后重新new一个新的BeanFactory,随后设置一些属性。其中最重要的则是loadBeanDefinitions这个方法,这个方法的作用则是加载所有的beanDefinition到本容器中去。

在 Spring Boot 中,loadBeanDefinitions方法的调用主要围绕 **AnnotatedBeanDefinitionReader** 展开,它是处理注解驱动配置(Spring Boot 的默认配置方式)的核心BeanDefinitionReader。其他BeanDefinitionReader(如XmlBeanDefinitionReader)仅在特定场景(如引入 XML 配置)中被使用,并非默认主选。

java 复制代码
// AnnotatedBeanDefinitionReader.java
@Override
public int loadBeanDefinitions(Class<?>... componentClasses) {
    Assert.notEmpty(componentClasses, "At least one component class must be specified");
    int count = 0;
    for (Class<?> componentClass : componentClasses) {
        // 注册单个注解类,本质是加载其BeanDefinition
        if (registerBean(componentClass)) { 
            count++;
        }
    }
  
}

// 内部调用registerBean,最终触发doRegisterBean解析注解并注册
private boolean registerBean(Class<?> beanClass) {
    doRegisterBean(beanClass, null, null, null, null);
    return true;
}

Spring 容器无法直接识别外部配置(如 @Component 注解、applicationContext.xml 中的 <bean> 标签),loadBeanDefinitions 的本质就是充当 "翻译官" 和 "注册员":

  1. 读取外部配置源:从指定的配置位置(如类路径、文件系统、注解扫描路径等)读取 Bean 的配置信息。
  2. 解析为 BeanDefinition :将配置信息(如 Bean 的类名、作用域、依赖关系、初始化方法等)解析为 Spring 内部标准的 BeanDefinition 对象(可理解为 "Bean 的元数据模板",包含了创建 Bean 所需的所有信息)。
  3. 注册到 BeanFactory :将解析后的 BeanDefinition 通过 BeanFactoryregisterBeanDefinition() 方法注册到工厂中,后续容器可通过 BeanFactory 查找、创建 Bean 实例。

loadBeanDefinitions 并非单一实现,而是通过不同的 BeanDefinitionReader(Bean 定义读取器) 适配多种配置方式

配置方式 对应的 BeanDefinitionReader 核心解析逻辑
XML 配置(如 <bean> XmlBeanDefinitionReader 解析 XML 文件中的 <bean> 标签、<context:component-scan> 等标签,提取 Bean 元数据。
注解配置(如 @Component AnnotatedBeanDefinitionReader + ClassPathBeanDefinitionScanner 1. 扫描指定包路径下的类;2. 识别带有 @Component@Service 等注解的类;3. 自动生成对应的 BeanDefinition
Java 配置类(如 @Configuration ConfigurationClassBeanDefinitionReader 解析 @Configuration 类中的 @Bean 方法,将每个 @Bean 方法转换为一个 BeanDefinition

总结

因此整个obtainFreshBeanFactory执行的逻辑则是,如果含有BeanFactory则销毁原来的并且重写创建一个新的BeanFactory,进行属性赋值随后调用loadBeanDefinitions 来完成BeanDefinition的加载,而这个BeanDefinition加载则是依靠BeanDefinitionReader来对一个个Bean进行封装为BeanDefinition。

prepareBeanFactory

在 Spring 框架中,prepareBeanFactoryAbstractApplicationContext 类中的一个核心方法,用于在容器初始化阶段对 BeanFactory(Spring 的核心 bean 容器)进行预处理和配置,为后续的 bean 定义加载、bean 实例化及依赖注入等操作奠定基础。

java 复制代码
// AbstractApplicationContext.java
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 1. 设置BeanFactory的类加载器(默认使用当前上下文的类加载器)
    beanFactory.setBeanClassLoader(getClassLoader());

    // 2. 设置表达式解析器(用于解析SpEL表达式,如@Value("#{systemProperties['user.name']}"))
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

    // 3. 注册属性编辑器注册表(用于将配置中的字符串转换为对应类型,如日期字符串→Date对象)
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 4. 添加ApplicationContextAwareProcessor后置处理器:
    // 用于处理实现了Aware接口的bean(如EnvironmentAware、ResourceLoaderAware等),
    // 在bean初始化时自动注入对应的上下文资源(如环境变量、资源加载器)
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    // 5. 忽略某些接口的自动依赖注入:
    // 这些接口的实现类依赖由ApplicationContextAwareProcessor手动注入,无需BeanFactory自动处理
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    // 6. 注册默认的依赖解析规则:
    // 当bean依赖以下类型时,自动注入容器自身的实例
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 7. 添加ApplicationListenerDetector后置处理器:
    // 用于检测实现了ApplicationListener接口的bean,并将其注册到容器的事件发布器中
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 8. 如果存在LoadTimeWeaver(用于类加载时织入,如AspectJ),则配置BeanFactory支持它
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // 设置临时类加载器,用于织入期间的类加载
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 9. 注册默认的环境相关bean(如果尚未注册):
    // 如environment(环境变量)、systemProperties(系统属性)、systemEnvironment(系统环境变量)
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

查看源码我们可以看到

首先是在BeanFactory当中设置了类加载器classloader,目的是为BeanFactory指定用于加载 bean 类定义的类加载器(ClassLoader)。

随后则是加入了表达式解析器和编辑器注册表这两个类的作用则是保证BeanFactory可以识别 Spring 表达式语言(SpEL)和将配置中的字符串 转换为目标类型(如DateResource等),归根结底则是为了支持注解(及其他配置方式)在依赖注入时的功能实现------ 前者用于解析表达式,后者用于处理类型转换,二者是注解生效的 "基础设施",但不负责 "识别注解" 本身。

然后则是addBeanPostProcessor添加了一个后置处理器,这个后置处理器ApplicationContextAwareProcessor的作用则是处理开发者自定义实现的Aware接口的实现类

之后则是调用了一系列的ignoreDependencyInterface方法用对于实现了上述Aware接口的 bean,不要对这些接口的方法进行自动依赖注入,目的是为了确保Aware接口的依赖注入逻辑由专门的处理器统一管理,避免注入混乱,保证bean.setXxx(...)回调的正确性,若BeanFactory对这些接口进行 "自动依赖注入"(比如通过类型匹配寻找依赖),会与ApplicationContextAwareProcessor的手动注入冲突,导致重复注入或错误

随后则是有一系列的registerResolvableDependency方法,

registerResolvableDependency(Class<?> type, Object value)的逻辑是:当容器中存在 bean 需要注入type类型的依赖时,无需从容器中查找该类型的 bean,直接使用value作为注入值

beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));随后则是注册事件监听器检测器到BeanFactory中去

  • ApplicationListenerDetector是一个BeanPostProcessor(后置处理器),会在 bean 初始化后执行:
    1. 检查 bean 是否实现了ApplicationListener
    2. 若是,则将其添加到ApplicationContext的事件发布器(ApplicationEventMulticaster)中,使其能接收并处理事件。
java 复制代码
 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // 设置临时类加载器,用于织入期间的类加载
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

当容器中存在LoadTimeWeaver(类加载时织入器)时,配置BeanFactory支持类加载阶段的字节码增强(如 AOP 切面织入、AspectJ 编译期增强)。支持需要在类加载阶段进行字节码增强的场景(如 AOP 的 "加载时织入"),扩展 Spring 的动态代理能力。

java 复制代码
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
// ... 注册systemProperties、systemEnvironment、applicationStartup

BeanFactory注册 4 个核心环境相关的单例 bean,供其他 bean 注入使用。提供 "开箱即用" 的环境配置访问能力,其他 bean 可通过@Autowired Environment env@Value("${systemProperties.user.name}")等方式直接使用这些核心配置对象,简化配置获取逻辑。

总结

prepareBeanFactory 方法的整体作用可以总结为:BeanFactory 构建完整的 "基础运行环境",通过配置核心能力、规范依赖规则、注册关键组件,使其从一个 "空白容器" 升级为能够支撑 Spring 核心功能(如注解驱动、依赖注入、事件机制等)的 "功能完备的 bean 管理中心"。具体可归纳为以下 5 点核心作用:

  1. 配置基础能力组件BeanFactory 装配类加载器、SpEL 表达式解析器、属性编辑器等 "基础设施",确保其能完成类加载、表达式求值、类型转换等基础操作(例如解析 @Value 注解中的表达式、将配置字符串转换为 Date 等类型)。

  2. 规范依赖注入规则

    • 明确 Aware 接口的依赖由专门处理器(ApplicationContextAwareProcessor)手动注入,避免 BeanFactory 自动注入冲突;
    • 注册容器核心组件(BeanFactoryApplicationContext 等)的依赖解析规则,让 bean 能便捷注入容器自身资源。
  3. 激活容器扩展机制 注册事件监听器检测器(ApplicationListenerDetector),自动识别并激活 ApplicationListener 接口的事件监听能力,支撑 Spring 的事件发布与订阅机制(如容器刷新、关闭等事件的处理)。

  4. 支持高级功能扩展 配置 LoadTimeWeaver 相关支持,为类加载时的字节码增强(如 AOP 织入、AspectJ 增强)提供基础,扩展容器的动态代理与切面能力。

  5. 注册核心环境组件 内置 Environment(环境配置)、系统属性、系统环境变量等核心对象作为单例 bean,供其他 bean 直接注入使用,简化配置访问与环境交互。

registerResolvableDependencyregisterSingleton的区别

  • registerResolvableDependency(Class<?> type, Object value) 用于注册 "依赖解析规则" :当容器需要注入type类型的依赖时,直接使用value作为注入值,而无需从容器中查找该类型的 bean。它本质是 **"定向依赖映射"**,不将value注册为容器中的正式 bean,仅用于解决特定类型的依赖注入。

  • registerSingleton(String beanName, Object singletonObject) 用于向容器注册一个正式的单例 bean :将singletonObjectbeanName为标识注册到容器中,使其成为容器管理的 bean,可通过getBean(beanName)获取,也会参与依赖注入、生命周期管理等。

维度 registerResolvableDependency registerSingleton
注册内容 注册 "类型→对象" 的映射规则,不将value作为 bean 管理 注册一个正式的单例 bean,singletonObject是容器中的 bean
是否为正式 bean value不被视为容器中的 bean,不会出现在getBeanNamesForType等结果中 singletonObject是正式 bean,可通过getBean获取
依赖注入场景 仅影响 "type类型依赖" 的注入,其他场景(如按名称获取)不受影响 既可以按类型注入,也可以按名称获取,完全参与 bean 生命周期
主要用途 解决 "容器核心组件(如BeanFactoryApplicationContext)的依赖注入",避免手动注册这些组件为 bean 注册系统级别的单例 bean(如Environment、系统属性等),供应用直接使用
相关推荐
兔兔爱学习兔兔爱学习1 天前
Spring Al学习7:ImageModel
java·学习·spring
lang201509281 天前
Spring远程调用与Web服务全解析
java·前端·spring
m0_564264181 天前
IDEA DEBUG调试时如何获取 MyBatis-Plus 动态拼接的 SQL?
java·数据库·spring boot·sql·mybatis·debug·mybatis-plus
崎岖Qiu1 天前
【设计模式笔记06】:单一职责原则
java·笔记·设计模式·单一职责原则
Hello.Reader1 天前
Flink ExecutionConfig 实战并行度、序列化、对象重用与全局参数
java·大数据·flink
熊小猿1 天前
在 Spring Boot 项目中使用分页插件的两种常见方式
java·spring boot·后端
paopaokaka_luck1 天前
基于SpringBoot+Vue的助农扶贫平台(AI问答、WebSocket实时聊天、快递物流API、协同过滤算法、Echarts图形化分析、分享链接到微博)
java·vue.js·spring boot·后端·websocket·spring
老华带你飞1 天前
机器人信息|基于Springboot的机器人门户展示系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·机器人·论文·毕设·机器人门户展示系统
notion20251 天前
Adobe Lightroom Classic下载与安装教程(附安装包) 2025最新版详细图文安装教程
java·数据库·其他·adobe
rengang661 天前
351-Spring AI Alibaba Dashscope 多模型示例
java·人工智能·spring·多模态·spring ai·ai应用编程