【老王读Spring IoC-5】Spring IoC 小结——控制反转、依赖注入

前言

前面从源码的角度逐步分析了 Spring IoC 的实现原理和技术细节。现在对其特性进行总结。
(总结总是能更好的消化知识,形成体系)

版本约定

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

Spring IoC 具有两大功能:

  1. 控制反转
  2. 依赖注入

控制反转

控制反转: 将 bean 的创建主动权交由 Spring 框架
要实现"控制反转"的功能,Spring 需要扫描出 bean 的定义(即 BeanDefinition),并将其存放到容器中(即 BeanDefinitionRegistry)。
有了 BeanDefinition 元数据信息,Spring 就可以创建 bean 的实例了,从而实现了控制权的反转。

控制反转的详细分析请移步 IoC之BeanDefinition扫描注册Spring bean的创建过程

依赖注入

依赖注入: 通过 @Autowired、@Resource 或者 xml 配置的方式定义 bean 的依赖,然后由 Spring 来注入依赖对象
要实"现依赖注入"的功能,Spring 需要解析出 bean 中的依赖属性(@Autowired、@Resource等标记的属性),然后通过 Spring “控制反转” 的能力创建依赖 bean 的实例,最后对依赖赋值。

依赖注入的详细分析请移步 IoC之依赖注入

AbstractApplicationContext#refresh()

在分析"控制反转"时,我们得知了 Spring 容器启动的入口是在 AbstractApplicationContext#refresh()
经过对"控制反转"和"依赖注入"的分析后,我们也了解了实现该功能相应的入口方法,现在对其进行标记,便于我们从更高的角度来观察 Spring IoC 的实现:

// AbstractApplicationContext#refresh()
public void refresh() throws BeansException, IllegalStateException {

    // 1. Prepare this context for refreshing.
    prepareRefresh();

    // 2. Tell the subclass to refresh the internal bean factory.
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 3. Prepare the bean factory for use in this context.
    prepareBeanFactory(beanFactory);

    // 4. Allows post-processing of the bean factory in context subclasses.
    postProcessBeanFactory(beanFactory);

    // 5. Invoke factory processors registered as beans in the context.
    // 执行 BeanFactoryPostProcessor。过程中会触发 BeanDefinition 的扫描注册。
    // 关键类: ConfigurationClassPostProcessor    
    invokeBeanFactoryPostProcessors(beanFactory);

    // 6. Register bean processors that intercept bean creation.
    registerBeanPostProcessors(beanFactory);

    // 7. Initialize message source for this context.
    initMessageSource();

    // 8. Initialize event multicaster for this context.
    initApplicationEventMulticaster();

    // 9. Initialize other special beans in specific context subclasses.
    onRefresh();

    // 10. Check for listener beans and register them.
    registerListeners();

    // 11. Instantiate all remaining (non-lazy-init) singletons.
    // 初始化所有非懒加载的 bean。(包括 bean 实例的创建和依赖属性填充)
    finishBeanFactoryInitialization(beanFactory);

    // 12. Last step: publish corresponding event.
    finishRefresh();
}

所以,"控制反转"的前戏"BeanDefinition 的扫描注册"的入口是在第 5 步 invokeBeanFactoryPostProcessors() 时触发的。
"依赖注入"的入口是在第 11 步 finishBeanFactoryInitialization() 时触发的,这里会初始化所有非懒加载的 bean,包括 bean 实例的创建和依赖属性的注入。

IoC 的完整过程

我们从上帝视角来看一下 IoC 的整个过程:

void ApplicationContext#refresh(){
    // 扫描出所有的 BeanDefinition
    DefaultListableBeanFactory beanFactory = scanBeanDefinitions();
    // 循环所有的 beanDefinitionNames
    for(String beanName: beanFactory.beanDefinitionNames){
        // 触发 bean 的实例化
        getBean(beanName);
    }
}


Object getBean(){
    // 创建 bean 的实例
    createBeanInstance(beanName);
    // 填充 bean 的依赖
    // 填充依赖的 bean 时,又会触发依赖 bean 的实例化,即 getBean(依赖beanName)
    populateBean(beanName);
}

上面使用伪代码的方式展现了整个 IoC 的粗略过程:

  1. 扫描出所有的 BeanDefinition,并注册到 BeanDefinitionRegistry 中
  2. 循环所有的 beanDefinitionNames,调用 getBean() 触发 bean 的实例化

bean 实例化的过程大体有两步:

  1. 创建 bean 的实例(通常是通过默认构造函数来创建)
  2. 填充 bean 的依赖(populateBean)
    在填充依赖 bean 时,又会触发依赖 bean 的实例化,也就是通过 getBean() 来进行触发。
    所以,这里会解决 bean 的连续依赖问题 和 循环依赖问题

连接依赖: A --> B --> C
循环依赖: A --> B --> A, 或者 A --> B --> C --> A

以上就是 Spring IoC 的大体骨架,这就是我所说的抓主干、放细节的方式来读源码,不要太拘泥于细节而不能自拔。
Spring 做为一个优秀的 IoC 框架,会在每个关键的地方设置扩展点,让基于框架的二次开发和定制变得容易。
比如:在扫描出所有的 BeanDefinition 之后,可以通过 BeanFactoryPostProcessor 来修改 BeanDefinition,包括增加、删除或者修改 BeanDefinition。
又比如:在 populateBean 完成之后,可以通过 BeanPostProcessor 来修改 bean 的实例对象。
像这些预留的扩展点,就是在这个骨架上的血肉,是来丰富 Spring IoC 的功能的。

小结

Spring 容器启动的入口是在 AbstractApplicationContext#refresh()

  • 控制反转
    “BeanDefinition 的扫描注册” 入口是在 refresh() 的第 5 步 invokeBeanFactoryPostProcessors() 时触发的,关键类是 ConfigurationClassPostProcessor
    bean 实例的创建是在 finishBeanFactoryInitialization() 时,加载 bean 时触发的。

  • 依赖注入
    依赖注入的入口是在 refresh() 的第 11 步 finishBeanFactoryInitialization() 时触发的。关键方法是 AbstractAutowireCapableBeanFactory#populateBean()
    populateBean() 方法会填充 bean 的依赖属性。

"控制反转"和"依赖注入"是密不可分的,它们共同组成了 Spring IoC 的核心功能。

整个 IoC 的粗略过程如下

  1. 扫描出所有的 BeanDefinition,并注册到 BeanDefinitionRegistry 中
  2. 循环所有的 beanDefinitionNames,调用 getBean() 触发 bean 的实例化

bean 实例化的过程大体有两步:

  1. 创建 bean 的实例(通常是通过默认构造函数来创建)
  2. 填充 bean 的依赖(populateBean)
    在填充依赖 bean 时,又会触发依赖 bean 的实例化,也就是通过 getBean() 来进行触发。
    所以,这里会解决 bean 的连续依赖问题 和 循环依赖问题

SpringIoC源码视频讲解:

课程地址
SpringIoC源码解读由浅入深https://edu.51cto.com/sd/68e86

如果本文对你有所帮助,欢迎点赞收藏!

源码测试工程下载:
老王读Spring IoC源码分析&测试代码下载
老王读Spring AOP源码分析&测试代码下载

公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…

阅读更多文章,请关注公众号: 老王学源码
gzh


系列博文:
【老王读Spring IoC-0】Spring IoC 引入
【老王读Spring IoC-1】IoC 之控制反转引入
【老王读Spring IoC-2】IoC 之 BeanDefinition 扫描注册
【老王读Spring IoC-3】Spring bean 的创建过程
【老王读Spring IoC-4】IoC 之依赖注入原理
【老王读Spring IoC-5】Spring IoC 小结——控制反转、依赖注入

相关阅读:
【Spring源码三千问】@Resource 与 @Autowired 的区别
【Spring源码三千问】bean name 的生成规则
【Spring源码三千问】BeanDefinition详细分析
【Spring源码三千问】Spring 是怎样解决循环依赖问题的?
【Spring源码三千问】哪些循环依赖问题Spring解决不了?
【Spring源码三千问】@Lazy为什么可以解决特殊的循环依赖问题?
【Spring源码三千问】BeanDefinition注册、Bean注册、Dependency注册有什么区别?
【Spring源码三千问】Bean的Scope有哪些?scope=request是什么原理?
【Spring源码三千问】为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值