# DI 详解
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
相关源码参考: Spring源码中文注释仓库5.3.x分支 (opens new window)
前文整体分析了refresh()的流程, 本章节针对 依赖注入 进行拆分
依赖注入的触发时机
依赖注入通常在 Bean 实例化时触发,具体情况如下:
非懒加载单例 Bean(lazy-init=false)
- 容器刷新阶段(
refresh())会通过finishBeanFactoryInitialization()的preInstantiateSingletons()遍历所有非懒加载单例 Bean - 对每个 Bean 调用
getBean()→ 触发实例化和依赖注入
- 容器刷新阶段(
懒加载单例 Bean(lazy-init=true)
- 容器启动阶段不会实例化
- 只有在 首次调用
getBean(beanName)时才触发实例化和依赖注入
原型 Bean
- 每次调用
getBean(beanName)都会创建新的实例,并触发依赖注入
- 每次调用
# 获取bean的入口
Spring IoC 容器的核心接口之一是 BeanFactory,它定义了容器的基本功能契约。BeanFactory 提供了多种 getBean() 方法,供用户获取管理的 Bean 实例。
实际调用时,getBean() 方法的具体实现集中在 AbstractBeanFactory 类中, 具体是在refresh#finishBeanFactoryInitialization(beanFactory)进行调用, 这一步也是真正做DI的地方。以下是 AbstractBeanFactory#getBean() 的核心逻辑:
AbstractBeanFactory#getBean(name)
│
└─ 调用 doGetBean(name, requiredType=null, args=null, typeCheckOnly=false)
│
├─ 1. 转换 Bean 名称
│ └─ beanName = transformedBeanName(name)
│
├─ 2. 从单例缓存获取实例
│ └─ sharedInstance = getSingleton(beanName)
│ ├─ 如果存在且 args == null
│ │ ├─ 判断是否循环依赖中
│ │ └─ 返回缓存的单例实例(sharedInstance)
│ │ └─ 如果是 FactoryBean,调用 getObjectForBeanInstance 获取真实对象
│ └─ 否则进入下一步
│
├─ 3. 检测当前是否正在创建原型 Bean,防止循环创建
│ └─ isPrototypeCurrentlyInCreation(beanName) -> 抛异常 if true
│
├─ 4. 如果当前容器无该 Bean 定义,尝试从父容器获取
│ ├─ 父容器存在且不包含 beanName
│ ├─ 根据父容器类型调用相应 getBean 方法递归获取
│ └─ 返回父容器获取的 Bean
│
├─ 5. 标记 Bean 为已创建(非类型检查阶段)
│ └─ markBeanAsCreated(beanName)
│
├─ 6. 记录启动监控步骤 applicationStartup.start("spring.beans.instantiate")
│
├─ 7. 获取合并后的 BeanDefinition
│ └─ mbd = getMergedLocalBeanDefinition(beanName)
│
├─ 8. 校验 BeanDefinition 和构造参数是否匹配
│ └─ checkMergedBeanDefinition(mbd, beanName, args)
│
├─ 9. 处理 dependsOn 依赖
│ ├─ 遍历 dependsOn 列表
│ ├─ 检测循环依赖,抛异常 if 存在
│ ├─ 注册依赖关系 registerDependentBean(dep, beanName)
│ └─ 递归 getBean(dep)
│
├─ 10. 根据作用域实例化 Bean
│ ├─ 如果是单例 Bean
│ │ └─ 调用 getSingleton(beanName, 创建 Bean 回调 createBean)
│ │ └─ (下一步详解)createBean(beanName, mbd, args)(实例化、依赖注入、初始化)
│ │ └─ 异常则销毁单例缓存
│ │ └─ FactoryBean,调用 getObjectForBeanInstance 获取真实对象
│ │
│ ├─ 如果是原型 Bean
│ │ ├─ beforePrototypeCreation(beanName)
│ │ ├─ createBean(beanName, mbd, args)
│ │ ├─ afterPrototypeCreation(beanName)
│ │ └─ FactoryBean,调用 getObjectForBeanInstance 获取真实对象
│ │
│ └─ 其他自定义作用域
│ ├─ 获取 Scope 对象
│ ├─ 调用 scope.get(beanName, 创建 Bean 回调 createBean)
│ ├─ beforePrototypeCreation 和 afterPrototypeCreation
│ └─ FactoryBean,调用 getObjectForBeanInstance 获取真实对象
│
├─ 11. 异常处理
│ ├─ 记录异常监控信息
│ ├─ cleanupAfterBeanCreationFailure(beanName)
│ └─ 抛出异常
│
└─ 12. 返回适配后的 Bean 实例(类型转换等)
└─ adaptBeanInstance(name, beanInstance, requiredType)
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
说明:
- Spring 容器中,单例模式(Singleton)的 Bean 在首次创建后会被缓存,后续直接从缓存中获取。
- 原型模式(Prototype)的 Bean 每次都会创建一个新的实例。
AbstractBeanFactory只负责整体的流程控制,具体的 Bean 实例创建逻辑由AbstractAutowireCapableBeanFactory#createBean()实现。
# 开始实例化
本节重点解析 Spring 容器中 Bean 创建的核心流程。通过源码可以看到,Spring 实例化 Bean 主要包含实例化对象和依赖注入两大关键步骤。
createBean(beanName, mbd, args)
│
├─ 1. 记录日志(trace 级别)
│
├─ 2. 确定 BeanDefinition 实际使用的类(resolveBeanClass)
│ ├─ 如果有解析到的类,克隆并设置真实类到 mbdToUse
│
├─ 3. 准备方法重写(prepareMethodOverrides)
│ └─ 校验 method-lookup、replace-method 的合法性
│
├─ 4. 扩展点1:解析初始化前代理(resolveBeforeInstantiation)
│ └─ 如果有代理对象,直接返回(跳过后续)
│
├─ 5. 核心创建流程(doCreateBean(beanName, mbdToUse, args))
│
│ doCreateBean(beanName, mbd, args)
│ │
│ ├─ 5.1 单例缓存检查(factoryBeanInstanceCache)
│ │ └─ 如果缓存中无,则调用 createBeanInstance 实例化 (下一步详解)
│ │
│ ├─ 5.2 获取实例和类型(instanceWrapper.getWrappedInstance)
│ │
│ ├─ 5.3 记录解析的类型(resolvedTargetType)
│ │
│ ├─ 5.4 合并后处理(applyMergedBeanDefinitionPostProcessors)
│ │ └─ 只执行一次(postProcessed)
│ │
│ ├─ 5.5 早期单例曝光(处理循环依赖)
│ │ ├─ 判断是否需要提前缓存
│ │ └─ 调用 addSingletonFactory 提前暴露引用
│ │
│ ├─ 5.6 依赖注入和初始化(下一步详解)
│ │ ├─ populateBean(属性填充和依赖注入)
│ │ └─ initializeBean(初始化生命周期回调、后置处理器)
│ │
│ ├─ 5.7 处理循环依赖后的代理替换(earlySingletonReference 逻辑)
│ │
│ └─ 5.8 注册销毁回调(DisposableBean 支持)
│
└─ 6. 返回初始化完成的 Bean 实例(exposedObject)
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
- 从上面的流程可以看出,Bean 的依赖注入主要发生在:
- createBeanInstance():负责通过构造函数或工厂方法创建 Bean 实例对象;
- populateBean():负责将 BeanDefinition 中的属性依赖注入到实例对象中。
# 创建 Bean 实例
Spring 中的 Bean 实例化支持多种策略,以适应不同的创建需求和场景,实现高度的灵活性和扩展性。常见的实例化策略包括:
| 策略类型 | 示例 |
|---|---|
| Supplier 实例化 | beanDefinition.setInstanceSupplier(() -> new UserService()) |
| 工厂方法实例化(Factory Method) | public static UserService createInstance() |
| 构造函数注入(Autowired 构造方法) | @Autowired public UserService(UserDao dao) |
| 无参构造函数实例化 | public UserService() |
实例化流程大致如下:
createBeanInstance(beanName, mbd, args)
│
├─ 1. 解析 Bean 的 Class 类型 (resolveBeanClass)
│ └─ 得到 beanClass
│
├─ 2. 判断 beanClass 是否为 public,且允许访问
│ ├─ 否 -> 抛 BeanCreationException 异常
│ └─ 是 -> 继续
│
├─ 3. 判断是否配置了实例化 Supplier(instanceSupplier)
│ ├─ 是 -> 调用 obtainFromSupplier(instanceSupplier, beanName) 实例化
│ └─ 否 -> 继续
│
├─ 4. 判断是否配置了工厂方法(factoryMethodName)
│ ├─ 是 -> 调用 instantiateUsingFactoryMethod(beanName, mbd, args) 实例化
│ └─ 否 -> 继续
│
├─ 5. 判断是否已经解析过构造方法(仅当 args == null 时)
│ ├─ 是,且需要自动装配构造方法 (autowireNecessary == true)
│ │ └─ 调用 autowireConstructor(beanName, mbd, null, null) 进行自动装配实例化
│ ├─ 是,但不需要自动装配
│ │ └─ 调用 instantiateBean(beanName, mbd) 使用无参构造函数实例化
│ └─ 否,未解析构造方法
│ ├─ 从后置处理器推断构造方法(determineConstructorsFromBeanPostProcessors)
│ ├─ 判断是否有推断出的构造方法、或自动装配模式为构造器注入,或存在构造参数
│ │ └─ 是 -> 调用 autowireConstructor(beanName, mbd, 推断的构造方法, args) 自动装配实例化
│ ├─ 否,判断是否有首选构造器(preferredConstructors)
│ │ └─ 是 -> 调用 autowireConstructor(beanName, mbd, 首选构造器, null) 实例化
│ └─ 否 -> 调用 instantiateBean(beanName, mbd) 使用无参构造函数实例化
│
└─ 6. 返回封装好的 BeanWrapper 实例
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
无参构造器实例化流程
instantiateBean(beanName, mbd)
│
├─ 1. 判断是否有安全管理器 (System.getSecurityManager)
│ ├─ 是 -> 通过 AccessController.doPrivileged 执行实例化
│ └─ 否 -> 直接调用实例化策略的 instantiate 方法
│
├─ 2. getInstantiationStrategy().instantiate(mbd, beanName, this)
│ │
│ ├─ 2.1 判断 BeanDefinition 是否有方法重写(hasMethodOverrides)
│ │ ├─ 有方法重写 -> 调用 instantiateWithMethodInjection(子类实现)
│ │ └─ 无方法重写 -> 继续
│ │
│ ├─ 2.2 同步锁住构造器缓存 (bd.constructorArgumentLock)
│ │ ├─ 尝试从缓存 bd.resolvedConstructorOrFactoryMethod 获取构造器
│ │ ├─ 如果缓存为空,执行以下步骤
│ │ │ ├─ 取得 Bean 的 Class(bd.getBeanClass())
│ │ │ ├─ 判断是否是接口
│ │ │ │ ├─ 是接口 -> 抛出 BeanInstantiationException 异常
│ │ │ │ └─ 不是接口 -> 继续
│ │ │ ├─ 判断安全管理器
│ │ │ │ ├─ 是 -> 使用 AccessController.doPrivileged 通过反射获取默认构造器
│ │ │ │ └─ 否 -> clazz.getDeclaredConstructor() 获取默认构造器
│ │ │ ├─ 缓存构造器到 bd.resolvedConstructorOrFactoryMethod
│ │ │
│ │ └─ 捕获异常 -> 抛出 BeanInstantiationException(无默认构造函数)
│ │
│ ├─ 2.3 调用 BeanUtils.instantiateClass(constructorToUse) 实例化对象
│
└─ 3. 返回实例化的 Bean 对象
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
说明:
- 如果 BeanDefinition 存在方法重写(如通过 Lookup 方法注入、替换方法注入),则实例化采用 CGLib 动态代理完成。
- 对于接口类型的 Bean,通常采用 JDK 动态代理实现。
- 无参构造函数实例化要求 Bean 类必须存在默认构造函数,否则实例化失败。
# 准备依赖注入
当 Bean 的实例创建完成后,Spring 会调用 populateBean() 方法,为该 Bean 填充属性,即完成依赖注入过程。
populateBean(beanName, mbd, instanceWrapper)
│
├─ 1. 判断是否为合成类(synthetic)
│ └─ 如果是且配置为跳过 Autowiring,直接返回,不进行填充
│
├─ 2. 获取所有 InstantiationAwareBeanPostProcessor
│ └─ 逐个执行 postProcessAfterInstantiation
│ ├─ 如果有返回 false,终止依赖注入
│ └─ 所有返回 true,继续执行
│
├─ 3. 获取原始属性值列表(PropertyValues pvs = mbd.getPropertyValues())
│
├─ 4. 判断自动装配模式(Autowire Mode)
│ ├─ BY_NAME → 调用 autowireByName(beanName, mbd, bw, pvs)
│ └─ BY_TYPE → 调用 autowireByType(beanName, mbd, bw, pvs)
│
├─ 5. 调用 BeanPostProcessor 的 postProcessProperties
│ └─ 遍历每个 InstantiationAwareBeanPostProcessor,合并返回值到 pvs
│
├─ 6. 判断是否有属性值需要注入
│ ├─ 是:
│ │ ├─ 类型转换(TypeConverter)
│ │ ├─ 解析属性依赖(resolveValueIfNecessary)
│ │ └─ 设置属性值(beanWrapper.setPropertyValues)
│ └─ 否:跳过注入
│
└─ 7. 属性填充完成,进入 initializeBean 初始化阶段
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
属性值类型与依赖解析说明
- 如果属性值是普通类型,且不需要类型转换,可直接注入。
- 如果属性值是对象引用、集合或包含类型转换需求的表达式,必须先进行解析,随后才能注入。
属性值的解析由 BeanDefinitionValueResolver 中的 resolveValueIfNecessary() 方法完成。
resolveValueIfNecessary(argName, value)
├── RuntimeBeanReference
│ ├─ 是否指向父容器?
│ │ ├─ 是:获取 parentBeanFactory → 找不到则抛异常
│ │ └─ 否:从当前容器中解析引用 Bean
│ └─ 如果引用的是 NullBean → 返回 null
│
├── RuntimeBeanNameReference
│ ├─ 获取并评估引用的 beanName
│ └─ 返回字符串形式的 bean 名称
│
├── BeanDefinitionHolder / BeanDefinition(内部匿名 Bean)
│ └─ 调用 resolveInnerBean 创建并注入该内部 Bean
│
├── DependencyDescriptor(如 @Autowired 注解)
│ ├─ 调用 resolveDependency 查找所需依赖
│ └─ 注册依赖关系
│
├── 集合类型封装(Spring 内部的 Managed 类型)
│ ├─ ManagedArray → resolveManagedArray
│ ├─ ManagedList → resolveManagedList
│ ├─ ManagedSet → resolveManagedSet
│ ├─ ManagedMap → resolveManagedMap
│ └─ ManagedProperties → 遍历每个键值进行解析
│
├── TypedStringValue(带目标类型的字符串)
│ ├─ evaluate() 计算表达式(SpEL 或 ${} 占位符)
│ └─ 类型转换为目标类型
│
├── NullBean
│ └─ 返回 null(用于显式设置属性为 null)
│
└── 默认分支
└─ 调用 evaluate(value) 执行表达式或直接返回值
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
当属性值依赖解析完成后开始进行赋值
# 注入赋值
Spring 在属性依赖解析完成后,会调用 setPropertyValues() 方法将属性注入到目标 Bean 实例中。其核心逻辑如下:
setPropertyValues(pvs, ignoreUnknown, ignoreInvalid)
├── 初始化 propertyAccessExceptions 为 null(收集异常)
├── 获取 propertyValues 列表
│ ├── 如果是 MutablePropertyValues → 取其 propertyValueList
│ └── 否则 → 转为数组后包装为 List
├── 如果 ignoreUnknown == true → 设置 suppressNotWritablePropertyException = true
├── 遍历每个 PropertyValue
│ ├── 尝试 setPropertyValue(pv)
│ ├── 捕获 NotWritablePropertyException
│ │ ├── 如果 ignoreUnknown 为 false → 抛出异常
│ │ └── 否则忽略
│ ├── 捕获 NullValueInNestedPathException
│ │ ├── 如果 ignoreInvalid 为 false → 抛出异常
│ │ └── 否则忽略
│ └── 捕获其他 PropertyAccessException
│ ├── 如果第一次出现 → 初始化异常列表
│ └── 加入异常集合
└── finally 块
└── 如果 suppressNotWritablePropertyException 被设置过 → 恢复为 false
└── 最后判断 propertyAccessExceptions 是否为空
├── 否 → 封装为 PropertyBatchUpdateException 并抛出
└── 是 → 正常结束
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
注入赋值机制说明
Spring 通过 BeanWrapper 对象对属性进行注入,赋值过程主要分为以下两类情况:
- 集合类型属性注入
对于
List、Set、Map等集合类型,Spring 会先解析所有元素,再统一转换为目标集合类型后注入属性中。 - 普通类型属性注入
对于普通对象属性,Spring 使用反射机制:
- 通过 getter 方法获取原始值(可选)。
- 通过 setter 方法注入新值(如 String、Integer、自定义对象等)。
至此,Spring IoC 容器完成了对 Bean 的定义读取 → 实例创建 → 依赖解析 → 属性赋值的完整流程。
Spring IoC 的控制反转与依赖注入机制,使得开发者无需手动创建和管理对象,只需声明依赖,Spring 会在合适的时机自动注入。这种解耦能力正是 Spring 框架的核心价值所在。