Spring 源码分析——IOC 容器


本篇文章讨论 Spring 源码中 IOC 的整体设计,对之前 Bean 生命周期创建的过程归纳整理。

对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/blob/main/docs/ioc

IOC 和 DI

  • IOC(Inversion of Control 控制反转): 是一种设计原则,降低组件之间的耦合度;在传统模型中对象要主动创建和管理其他对象,进而会造成高耦合度和难以扩展的问题。而 IOC 通过将对象创建和管理的控制权反转到容器中,使得对象只需要定义自己所需的依赖,并由容器来负责实例化和注入依赖。
  • DI(Dependency Injection 依赖注入): 是 IOC 的一种具体实现方式,用于解决对象之间的依赖关系;在 DI 中,对象不再直接依赖于其他对象,而是将依赖关系外部化,并通过容器注入所需的依赖;Spring 中的 DI 主要通过构造函数注入、Setter 方法注入等方式实现,以及基于注解的自动装配(Autowired)。

控制了什么?反转了什么?

IOC 将对象之间的依赖关系的控制权从应用程序代码中反转到容器中。换句话说 IOC 控制了对象的生命周期和依赖关系,而不是让对象自行创建和管理其他对象;

因此,IOC 的控制反转锁控制的是对象的依赖关系和生命周期,并且将这个控制权从应用程序代码中反转到容器中;它将原本由对象自行控制的部分交给了外部容器控制,从而实现了对依赖关系和生命周期的控制反转。

IOC 和 Bean

根据上面提到了 IOC 容器,其实 IOC 容器其实就是 Spring 中管理 Bean 的容器(具体来说其实是多个 Map 对象、三级缓存等一系列 Map 对象容器);因此我们在前【1-12】章节说到的 Bean 生命周期范畴其实都是 IOC 的阶段。

Spring 官方文档中对 IOC 的解释如下:

Spring 源码分析——IOC 容器
img

总结一下:

  • beanscontext包是 IOC 的基础,beans包的核心是 BeanFactory 接口,context包的核心是 ApplicationContext 接口;
  • ApplicationContext 接口继承 BeanFactory 接口,具有 BeanFactory 的接口的全部功能;
  • BeanFactory 提供了配置框架和基础功能,ApplicationContext 接口则增加了更多企业定制功能;
  • 描述 Bean 元数据配置通过 XML、Java 注解、Java 代码,例如 XML 的<bean>标签,或@Autowire注解;
  • getBean()函数定义在 BeanFactory,createBean()函数定义在 AbstractBeanFactory,getSingleton()函数定义在 SingletonBeanRegistry;
  • Bean 创建实例的核心是 AbstractAutowireCapableBeanFactory 类,其中包含了三级缓存的处理,负责 Bean 实例化和初始化过程;
  • ApplicationContext 是 Spring 的应用上下文,它提供了 Spring 开箱即用的几个接口实现,核心是refresh接口;
  • 传统用 XML 配置 Bean 元数据是通过创建 ClassPathXmlApplicationContext 实例对象加载的,但是通常都使用注解完成,Spring 会根据属性信息自动配置;

IOC 体系下 Bean 加载流程的几个关键:

  1. ApplicationContext 作为入口,Spring 容器启动时便会加载refresh()函数预先装配容器运行所必须的 Bean 实例(注意我们业务代码用@Component、@Service 标记的 Bean 并不会被立即加载,而是用到才会被创建实例,即延迟加载);
  2. 接在通过 XML、注解、代码等方式配置 Bean 元数据和依赖关系的 BeanDefinition 对象;
  3. BeanFactory 接口获取getBean,AbstractAutowireCapableBeanFactory 类中有具体的实例化过程,默认通过 Cglib 动态代理生成 Bean 实例;存储 Bean 的容器本质都是一系列的 Map 对象;
  4. Bean 实例化、初始化前后会加载各种 Aware 和 BeanpostProcessor 处理器以此影响 Bean 实例化、初始化结果;
  5. Spring 通过 Event 事件机制维护整个上下文容器启动、刷新等事件通知;

回顾

回顾整个 IOC 阶段涉及到核心的接口和类:

Spring 源码分析——IOC 容器
img

接着,从 BeanFactory 接口维度描述下 Bean 的加载流程:

  1. 通过 Resource 接口解析 XML 文件、注解等方式拿到 Bean 实例信息以及依赖关系;
  2. 通过 InstantiationStrategy 接口拿到 Bean 实例化策略,Spring 默认采用 Cglib 动态代理策略生成 Bean 实例;
  3. 通过 BeanDefinitionReader 接口封装 Bean 实例化所需的 BeanDefinition 对象;
  4. 进行 Bean 的实例化,实例化后的单例 Bean 会被放入 singletonMap 容器中;(1-3)
  5. 注入 BeanFactoryPostProcessor,作用与 Bean 实例化前,可以动态改变 BeanDefinition 结果,以此影响 Bean 实例化
  6. 进行 Bean 的初始化,初始化过程包括对全局 Bean 实例化后的配置检查等;
  7. 注入 BeanPostProcessor,作用与 Bean 初始化前后,可以动态改变 Bean 的初始化信息;
  8. 完成 Bean 的实例化、初始化(此时 Bean 的加载流程已经结束)

然后再从 ApplicationContext 上下文的层面分析整个启动流程:

  1. 实例化 BeanFactory 对象并加载所有 BeanDefinition 信息;
  2. 注入各种 BeanPostProcessor 对象,并在 Bean 实例化前和初始化前后过程中影响实例化和初始化结果
  3. 在注入 BeanPostProcessor 的同时注入各种 Aware 接口,使得 Spring 容器能感知到外部需要获取的容器对象;
  4. 注入 Spring 的事件机制,保证在 ApplicationContext 的初始化、刷新、关闭时监听到事件变化;
  5. 以上对象加载完毕后最后再进行 Bean 的实例化,进行上面的 Bean 加载流程;

最后,通过简单的流程图描述下上述流程:

Spring 源码分析——IOC 容器
img

Spring 源码专栏

此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题

配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs

联系我

  • 个人博客:http://tycoding.cn/
  • GitHub:https://github.com/tycoding
  • 微信公众号:程序员涂陌
  • QQ 交流群:866685601


原文始发于微信公众号(程序员涂陌):Spring 源码分析——IOC 容器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145548.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!