第一步:容器启动阶段

加载配置文件过程(Web 环境)

  • 在 Spring 的主配置文件 web.xml 中配置 ContextLoaderListener 监听器,并通过 <context-param> 指定 Spring 配置文件位置:
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  • 当 Servlet 容器(如 Tomcat)启动时:
    • 为 Web 应用创建全局上下文环境 ServletContext
    • 容器调用 web.xml 中配置的 ContextLoaderListener.contextInitialized() 方法;
    • 该方法初始化 WebApplicationContext 上下文(即 IoC 容器,默认实现为 XmlWebApplicationContext);
    • 加载 context-param 指定的配置文件信息到 IoC 容器中;
    • 最终,WebApplicationContext 实例以键值对形式保存在 ServletContext 中(键为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)。
  • 至此,IoC 容器的初始化正式开始。

通过 refresh() 方法启动整个容器(通用入口)

  • 无论是 Web 环境还是独立应用,创建 ApplicationContext 容器实例(如 AnnotationConfigApplicationContextClassPathXmlApplicationContext)后,都会调用其 refresh() 方法来启动整个容器。
  • refresh() 是容器初始化的统一入口,位于 synchronized 同步代码块中,确保多线程环境下容器只能被初始化一次。

第二步:BeanDefinition 的注册阶段

a. 资源定位(Resource Location)

  • 容器通过 ResourceLoader 将配置路径(如 "classpath:beans.xml")解析为 Resource 对象。

Resource 是 Spring 对外部资源(类路径、文件系统、URL 等)的统一抽象(具体实现如 ClassPathResourceFileSystemResource),仅表示资源的位置,尚未执行任何 I/O 操作

  • 此阶段由 ResourceLoader 完成,为后续读取操作提供资源句柄。

b. 资源载入与解析(Loading & Parsing)

  • 使用对应的 BeanDefinitionReader(如 XmlBeanDefinitionReader)读取 Resource 的输入流,解析其内容(如 XML 中的 <bean> 元素),生成结构化的 BeanDefinition 对象。
  • 具体解析流程如下(源码级细节):
    • Reader 读取器(如 XMLBeanDefinitionReader.loadBeanDefinitions() 方法)中,调用 documentLoader(底层 XML 解析器)的方法,获得 DOM Document 对象;
    • processBeanDefinition(Element, BeanDefinition) 方法内部调用 parseBeanDefinitionElement(),逐层解析具体的 XML 标签(如 <bean><id><name><property> 等),提取属性值,并将解析结果封装到 BeanDefinitionHolder 中(BeanDefinitionHolderBeanDefinition 的包装类,包含 BeanDefinition、bean 名称、别名等信息,用于后续注册)。
    • 接着,在 Reader 中调用 processBeanDefinition() 方法,传入 Document 的根元素(Element);
  • 经过对 Document 的逐层解析,XML 中定义的 Bean 配置被完整转换为 Spring 内部的 BeanDefinition 数据结构。
  • 注意:此时仅完成元数据建模,依赖注入尚未发生,容器中存储的只是静态配置信息。

c. 注册(Registration)

  • 将解析得到的 BeanDefinition 以 bean 名称为 key,注册到 BeanFactory 内部的 ConcurrentHashMap<String, BeanDefinition> 中,供后续实例化使用。
  • 注册的具体步骤如下:
    • 在第 8 步的 processBeanDefinition() 方法中,调用 registerBeanDefinition(BeanDefinitionHolder) 开始注册;
    • 该方法最终执行 map.put(beanName, beanDefinition),将 BeanDefinition 存入注册表;
    • registerBeanDefinition() 方法内部使用 synchronized 同步代码块,保证并发环境下的数据一致性。
  • 以上整个过程均由 BeanDefinitionReader(如 XmlBeanDefinitionReader)在其 loadBeanDefinitions() 方法中完成。

Resource 定位与 refresh() 调用链(源码上下文)

整个 BeanDefinition 注册流程在 refresh() 方法中触发,具体调用链如下:

refresh()
  └─ obtainFreshBeanFactory()
      └─ refreshBeanFactory() (由 ClassPathXmlApplicationContext 重写实现)
          └─ loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
              ├─ getResourceLoader().getResource("applicationContext.xml") → Resource(资源定位)
              └─ new XmlBeanDefinitionReader(beanFactory).loadBeanDefinitions(resource)(载入+解析+注册)

在构造 Spring IoC 容器时,首先需要明确 BeanDefinition 的来源(例如一个 XML 配置文件)。
Spring 通过 Resource 接口对各类外部资源进行统一抽象(如 ClassPathResource 表示类路径下的资源,FileSystemResource 表示文件系统中的资源)。
此时,容器仅完成资源定位,尚未读取或解析其内容。
真正的资源加载由 ResourceLoader 负责:它根据配置路径(如 "classpath:applicationContext.xml")解析出对应的 Resource 对象,为后续 I/O 操作提供基础。

容器初始化的核心流程(细化)

  1. 调用 refresh() 方法:该方法位于 synchronized 同步块中,确保容器初始化过程线程安全;
  2. 在 refresh()方法内部,创建 BeanFactory:在 obtainFreshBeanFactory() 内部,调用子类实现的 refreshBeanFactory(),其中通过 createBeanFactory() 创建一个 DefaultListableBeanFactory 实例;
  3. 加载 BeanDefinition:随后调用 loadBeanDefinitions(BeanFactory),该方法由具体容器子类(如 ClassPathXmlApplicationContext)实现;
  4. 资源定位:loadBeanDefinitions() 首先通过 ResourceLoader 将配置路径转换为一个或多个 Resource 对象(即完成定位);
  5. 资源读取与解析:根据配置类型选择对应的 BeanDefinitionReader(例如 XmlBeanDefinitionReader 用于 XML 配置,或通过 ClassPathBeanDefinitionScanner 扫描注解类),解析 Resource 中的内容。解析过程中,每个 <bean> 元素或每个被组件扫描发现的类,都会被转换为一个独立的 BeanDefinition 对象,其中包含该 bean 的类名、作用域、依赖关系、属性值等元数据。
  6. 最后,这些 BeanDefinition 实例会被注册到 BeanFactorybeanDefinitionMap 中,以 bean 名称(bean name)为 keyBeanDefinition 为 value 进行存储,供后续的 bean 实例化和依赖注入使用。

第三步:BeanFactory的初始化后续内容

public void refresh() {
    // 1. 准备刷新(如设置启动时间、激活标志等)
    prepareRefresh();

    // 2. 【创建 BeanFactory】并【加载 BeanDefinition】→ 此时 BeanDefinition 被注册到 map 中
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 3. 配置 BeanFactory(设置类加载器、表达式解析器、忽略接口等)
    prepareBeanFactory(beanFactory);

    // 4. 【允许子类扩展 BeanFactory】(如注册 BeanPostProcessor 等)
    postProcessBeanFactory(beanFactory);

    // 5. 【调用 BeanFactoryPostProcessor】 → 可以修改已注册的 BeanDefinition!
    invokeBeanFactoryPostProcessors(beanFactory);

    // 6. 【注册 BeanPostProcessor】(注意:不是执行,只是注册)
    registerBeanPostProcessors(beanFactory);

    // 7. 初始化消息源、事件广播器等(ApplicationContext 特有)

    // 8. 【重点!】实例化所有非懒加载的单例 Bean
    finishBeanFactoryInitialization(beanFactory); // ← 在这里才真正创建 Bean!

    // 9. 完成刷新(发布事件等)
    finishRefresh();
}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐