本篇文章讨论 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 的解释如下:

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

接着,从 BeanFactory 接口维度描述下 Bean 的加载流程:
-
通过 Resource 接口解析 XML 文件、注解等方式拿到 Bean 实例信息以及依赖关系; -
通过 InstantiationStrategy 接口拿到 Bean 实例化策略,Spring 默认采用 Cglib 动态代理策略生成 Bean 实例; -
通过 BeanDefinitionReader 接口封装 Bean 实例化所需的 BeanDefinition 对象; -
进行 Bean 的实例化,实例化后的单例 Bean 会被放入 singletonMap 容器中;(1-3) -
注入 BeanFactoryPostProcessor,作用与 Bean 实例化前,可以动态改变 BeanDefinition 结果,以此影响 Bean 实例化 -
进行 Bean 的初始化,初始化过程包括对全局 Bean 实例化后的配置检查等; -
注入 BeanPostProcessor,作用与 Bean 初始化前后,可以动态改变 Bean 的初始化信息; -
完成 Bean 的实例化、初始化(此时 Bean 的加载流程已经结束)
然后再从 ApplicationContext 上下文的层面分析整个启动流程:
-
实例化 BeanFactory 对象并加载所有 BeanDefinition 信息; -
注入各种 BeanPostProcessor 对象,并在 Bean 实例化前和初始化前后过程中影响实例化和初始化结果 -
在注入 BeanPostProcessor 的同时注入各种 Aware 接口,使得 Spring 容器能感知到外部需要获取的容器对象; -
注入 Spring 的事件机制,保证在 ApplicationContext 的初始化、刷新、关闭时监听到事件变化; -
以上对象加载完毕后最后再进行 Bean 的实例化,进行上面的 Bean 加载流程;
最后,通过简单的流程图描述下上述流程:

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