# 不同IOC的流程解析

作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
相关源码参考: Spring源码中文注释仓库5.3.x分支 (opens new window)


在手写 Spring 框架加深理解之后,我们开始系统分析 Spring 源码。 后续源码分析中代码量较大,这里不贴完整源码,请参考 Spring 5.3.x 官方源码或笔者仓库。

切记: 跟着流程多 debug,多观察方法调用顺序!

# 一、Spring 框架体系

# BeanFactory

Spring Bean 的创建是典型的工厂模式,一系列 Bean 工厂共同构成 IoC 容器体系。

接口体系最顶层:

  1. BeanFactory : 最顶层工厂接口,定义了IOC 容器的基本功能规范。

  2. 核心子接口:

    • ListableBean Factory: 表示容器中的 Bean 可被枚举和查询。
    • Hierarchical BeanFactory: 支持父子容器结构,Bean 可继承。
    • AutowireCapableBeanFactory: 支持对 Bean 的自动装配(依赖注入)。

    这些接口共同定义了 Bean 的集合关系、继承结构以及注入行为,最终由 DefaultListableBeanFactory 提供完整实现,作为 Spring 默认的核心 IoC 容器。

提示BeanFactory 提供基础的 Bean 获取、类型判断、单例/原型判断等能力。具体源码可查阅笔者仓库。

  • 补充说明
    • BeanFactory 是最小化功能的容器,强调懒加载(延迟初始化),适合轻量级场景。
    • ApplicationContextBeanFactory 的子接口,提供更丰富的应用框架功能,如国际化、事件发布、AOP 支持等,本章不展开,后续章节将详解。

# BeanDefinition

BeanDefinition 描述 Bean 的配置信息,如类名、作用域、构造参数等。 Spring 中 BeanDefinition 继承体系较为复杂,是容器管理 Bean 配置信息的核心数据结构。

# BeanDefinitionReader

BeanDefinitionReader 负责解析 Spring 配置文件,并将配置信息转为 BeanDefinition。 Spring 提供多种实现,支持 XML、注解、Properties 等格式。


# 二、IOC 容器初始化流程

本章节解析三种常见初始化方式:Web、XML、注解

# 1 Web IOC 初始化准备工作

在手写源码中,Web IOC 容器的初始化逻辑主要集中在 DispatcherServletinit() 方法中。需要注意的是,init() 方法重写自父类 HttpServletBean,但其核心初始化逻辑在 initServletBean() 方法里完成。

# 入口

Servlet 容器启动,调用 DispatcherServlet.init(),实际调用父类 HttpServletBean.init()(模板方法)

关键点

  • Servlet 容器调用 Servlet.init(ServletConfig)
  • HttpServletBean 继承 Servlet,重写 init(ServletConfig)
  • HttpServletBean.init() 做通用初始化,调用抽象模板方法 initServletBean()
  • DispatcherServlet 继承自 HttpServletBean,重写 initServletBean() 实现具体初始化。
Servlet 容器调用
       │
       ▼
HttpServletBean.init(ServletConfig)   ←—— 父类统一入口
       │
       │-- 解析 init-param 注入属性
       │
       │-- 调用 initServletBean() (抽象方法)
       ▼
DispatcherServlet.initServletBean()  ←—— 子类具体初始化

1
2
3
4
5
6
7
8
9
10
11

# 核心初始化方法

initServletBean()方法是 Spring Web 容器启动的核心控制点,管理整个生命周期。

DispatcherServlet.initServletBean()
       │
       │-- 记录初始化日志
       │
       │-- 调用 initWebApplicationContext()
       │
       │-- 调用 initFrameworkServlet()
       │
       └─ 记录初始化完成日志
1
2
3
4
5
6
7
8
9

说明:

  • initWebApplicationContext() 获取或创建 Spring Web 容器。
  • initFrameworkServlet() 可执行其他初始化操作(可选)。

# 获取或创建 WebApplicationContext

FrameworkServlet.initWebApplicationContext()
       │
       │-- 从 ContextLoaderListener 获取根容器
       │       (WebApplicationContextUtils.getWebApplicationContext)
       │
       │-- 判断 DispatcherServlet 是否已有 WebApplicationContext
       │       ├─ 有且未激活,则调用 configureAndRefreshWebApplicationContext()
       │       │           │
       │       │           └─ 调用 ConfigurableWebApplicationContext.refresh()
       │       │                (IOC 容器启动)
       │       ├─ 有且已激活,直接使用
       │       └─ 无,则创建新的 WebApplicationContext 并刷新
       │               (createWebApplicationContext() + configureAndRefreshWebApplicationContext())
       │                       │
       │                       └─ 调用 ConfigurableWebApplicationContext.refresh()
       │                            (IOC 容器启动)
       │
       │-- 发布 WebApplicationContext 到 ServletContext
       │       (ServletContext.setAttribute)
       │
       └─ 调用 onRefresh(WebApplicationContext) 初始化 Spring MVC 组件
               (FrameworkServlet.onRefresh() -> DispatcherServlet.onRefresh())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

说明:

  • refresh() 是 IOC 容器启动的核心入口,完成 Bean 的加载、实例化和依赖注入。
  • onRefresh() 初始化 MVC 核心组件,如 HandlerMapping、ViewResolver 等, 即refresh()中留给子类实现的方法。

至此,Web IOC 初始化准备完成。


# 2 XML 的 IOC 初始化准备工作

XML IOC 初始化主要包含三个步骤:BeanDefinition 的资源定位、加载和注册。 以 ApplicationContext 为例,Web 项目中常用 WebApplicationContext 是其子类。

ApplicationContext 支持上下文的嵌套,通过维护父上下文形成一个上下文体系。进行 Bean 查找时,首先在当前上下文中查找,若未找到,则逐级向上查找父上下文。这样设计为多个 Spring 应用提供了共享的 Bean 定义环境。

# 容器启动入口与配置路径解析

用户调用 new ClassPathXmlApplicationContext("application.xml")
       │
       ├─ 触发 ClassPathXmlApplicationContext 构造器
       │       ├─ 设置父容器(支持继承和复用 Bean)
       │       │
       │       ├─ 调用父类 AbstractApplicationContext 构造方法
       │       │       ├─ 初始化 ResourcePatternResolver(默认 PathMatchingResourcePatternResolver)
       │       │       │       └─ 负责解析 classpath*:classpath: 等资源路径格式
       │       │       │
       │       │       └─ 其他基础上下文初始化操作
       │       │
       │       ├─ 调用 setConfigLocations()
       │       │       ├─ 校验并规范配置文件路径
       │       │       ├─ 解析占位符(如 ${user.home})
       │       │       └─ 将路径转为统一格式,方便资源加载
       │       │
       │       └─ (如果参数 refresh=true)自动调用 refresh() 启动容器初始化
       │
       └─ 容器完成基础资源定位和初始化准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

说明:

  • 所有 XML/注解上下文类都继承自 AbstractApplicationContext,它通过 装饰器模式 + 策略模式 完成统一初始化逻辑,并最终调用 refresh() 方法完成容器刷新。

  • 在构造阶段,Spring 已将配置文件路径封装为 Resource 对象,完成 资源定位

  • 但此时 Bean 尚未解析成 BeanDefinition,真正的解析与注册操作要等到 refresh() 调用 BeanDefinitionReader 时才执行。


# 3 Annotation 的IOC 初始化准备工作

Spring 对注解的定位

  • 类级别注解@Component@Repository@Controller@Service
  • 类内部注解@Autowired@Value@Resource

容器及注解处理

  • 主要容器:AnnotationConfigApplicationContext(普通)和 AnnotationConfigWebApplicationContext(Web 版本)
  • 本节以 AnnotationConfigApplicationContext 为例进行源码分析

# 构造器调用及初始化流程

【外部调用】
AnnotationConfigApplicationContext(...)
        │
        ├─► 构造器参数判断
        │
        ├─ 无参构造 → AnnotationConfigApplicationContext()
        │     │
        │     ├─ 初始化 reader = new AnnotatedBeanDefinitionReader(this)
        │     └─ 初始化 scanner = new ClassPathBeanDefinitionScanner(this)
        │
        ├─ (下一步详解)注解类参数 → AnnotationConfigApplicationContext(Class<?>... componentClasses)
        │     │
        │     ├─ 调用无参构造器 ← 初始化 reader & scanner
        │     ├─ register(componentClasses)
        │     └─ refresh() // 容器刷新,完成 Bean 初始化
        │
        └─ (下一步详解)包路径参数 → AnnotationConfigApplicationContext(String... basePackages)
              │
              ├─ 调用无参构造器 ← 初始化 reader & scanner
              ├─ scan(basePackages)
              └─ refresh()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

两种常用注册方式

  1. 直接注册注解 Bean
    • 构造器传入具体的 Bean 类
    • BeanDefinition 在构造阶段注册,后续 refresh() 仅做后续初始化(如 BeanPostProcessor、单例 Bean 实例化等)
  2. 扫描包路径自动注册
    • 构造器传入基础包路径
    • 扫描并注册所有候选注解组件
    • 如果容器已创建后新增 Bean,需要手动调用 scan() 并刷新

# 直接注册注解 Bean 的流程

AnnotationConfigApplicationContext(Class<?>... componentClasses)
│
├─ 1. 初始化
│     └─ 调用 this(),初始化 reader 和 scanner
│
├─ 2. 注册组件 register(componentClasses)
│     └─ 调用 this.reader.register(componentClasses)
│           │
│           └─ 遍历每个 componentClass
│                 └─ registerBean(componentClass)
│                       └─ doRegisterBean()
│                             │
│                             ├─ 创建 AnnotatedGenericBeanDefinition(abd)
│                             │     └─ 读取注解元信息
│                             │
│                             ├─ 判断是否跳过注册(@Conditional 条件判断)
│                             │
│                             ├─ 设置实例供应商 supplier(可选)
│                             │
│                             ├─ 解析 Scope 元数据(resolveScopeMetadata)
│                             │     ├─ 获取 @Scope 注解属性
│                             │     └─ 设置 scopeName 和 proxyMode
│                             │
│                             ├─ 生成 Bean 名称
│                             │     └─ 使用 this.beanNameGenerator.generateBeanName(...)
│                             │
│                             ├─ 处理通用注解(@Lazy、@Primary、@DependsOn 等)
│                             │     └─ AnnotationConfigUtils.processCommonDefinitionAnnotations(abd)
│                             │
│                             ├─ 处理限定符注解(如 @Qualifier)
│                             │
│                             ├─ 应用自定义 BeanDefinitionCustomizer(可选)
│                             │
│                             ├─ 封装为 BeanDefinitionHolder
│                             │
│                             ├─ 创建作用域代理(ScopedProxyMode)
│                             │     └─ AnnotationConfigUtils.applyScopedProxyMode(...)
│                             │
│                             └─ 注册 BeanDefinition 到容器(BeanDefinitionRegistry)
│                                   └─ BeanDefinitionReaderUtils.registerBeanDefinition(...)
│                                         ├─ registry.registerBeanDefinition(beanName, definition)
│                                         └─ registry.registerAlias(...)
│
└─ 3. 刷新容器 refresh()
      └─ 完成 Bean 实例化、依赖注入和生命周期管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 扫描包路径自动注册流程

AnnotationConfigApplicationContext(String... basePackages)
│
├─ 1. 初始化容器
│     └─ 调用无参构造器 this()
│
├─ 2. 执行扫描 scan(basePackages)
│     └─ 调用 scanner.scan(basePackages)
│           │
│           ├─ doScan(basePackages)
│           │     ├─ 遍历每个 basePackage
│           │     ├─ 查找带注解的候选组件(findCandidateComponents)
│           │     ├─ 解析 Scope 元数据(ScopeMetadata)
│           │     ├─ 生成 Bean 名称(使用 BeanNameGenerator)
│           │     ├─ 处理通用注解(@Lazy、@Primary 等)
│           │     ├─ 检查 Bean 名称冲突(checkCandidate)
│           │     └─ 注册 BeanDefinition(调用 BeanDefinitionReaderUtils.registerBeanDefinition)
│           │
│           ├─ 注册注解处理器(AnnotationConfigUtils.registerAnnotationConfigProcessors)
│           │     ├─ @AutowiredAnnotationBeanPostProcessor
│           │     ├─ @CommonAnnotationBeanPostProcessor
│           │     └─ @ConfigurationClassPostProcessor 等
│           │
│           └─ 返回已注册的 BeanDefinition 数量
│
└─ 3. 刷新容器 refresh()
      └─ 完成 Bean 的实例化、依赖注入和生命周期管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 注解 Bean 与 XML Bean 注册对比

  • 注解 Bean:在构造阶段通过 register() 或构造器完成注册
  • XML Bean:在 refresh() 调用 obtainFreshBeanFactory() 时注册

为什么注解 Bean 不会重复注册?

  1. AnnotationConfigApplicationContext 构造时已创建 DefaultListableBeanFactory
  2. 调用 refresh() 时执行:
refreshBeanFactory(); // GenericApplicationContext 空实现
return getBeanFactory(); // 返回已初始化 BeanFactory
1
2
  1. refreshBeanFactory() 不重建 BeanFactory,也不重新注册 BeanDefinition,只设置序列化 ID

因此,构造阶段完成注册后,refresh() 仅做 Bean 后处理和实例化,避免重复注册。

在 Spring Boot 中,启动类和配置类同样在 register() 阶段注册,refresh() 主要负责配置解析、自动配置展开和 Bean 实例化。

至此, 不同类型IOC容器的准备工作就绪, 开始进行容器的刷新流程refresh()