IOC 是什么?
Spring IOC(Inversion of Control,控制反转)是 Spring 框架的核心概念之一。它是通过依赖注入(Dependency Injection) 实现的。IOC 让对象的创建与管理职责由容器负责,而不是由对象自身控制。
核心思想:控制反转意味着将对象的创建和依赖关系交由 Spring 容器管理,而不是由程序代码直接控制。这种机制使得程序更加灵活和解耦,提升了代码的可维护性和扩展性。
依赖注入:通过构造器注入、setter 注入或接口注入,将对象所需的依赖传递给它,而不是让对象自行创建依赖。
理解控制和反转
IOC,即 Inversion of Control,控制反转。
首先要明确 IOC 是一种思想,而不是一个具体的技术,其次 IOC 这个思想也不是 Spring 创造的。
然后我们要理解到底控制的是什么,其实就是控制对象的创建,IOC 容器根据配置文件来创建对象,在对象的生命周期内,在不同时期根据不同配置进行对象的创建和改造。
那什么被反转了?其实就是关于创建对象且注入依赖对象的这个动作,本来这个动作是由我们程序员在代码里面指定的,例如对象 A
依赖对象 B
,在创建对象 A
代码里,我们需要写好如何创建对象 B
,这样才能构造出一个完整的 A
。
而反转之后,这个动作就由 IOC 容器触发,IOC 容器在创建对象 A
的时候,发现依赖对象 B
,根据配置文件,它会创建 B
,并将对象 B
注入 A
中。
这里要注意,注入的不一定非得是一个对象,也可以注入配置文件里面的一个值给对象 A
等等。
至此,你应该明确了,控制和反转。
Spring IOC 有什么好处?
对象的创建都由 IOC 容器来控制之后,对象之间就不会有很明确的依赖关系,使得非常容易设计出松耦合的程序。
例如,对象 A
需要依赖一个实现 B
,但是对象都由 IOC 控制之后,我们不需要明确地在对象 A
的代码里写死依赖的实现 B
,只需要写明依赖一个接口,这样我们的代码就能顺序的编写下去。
然后,我们可以在配置文件里定义 A
依赖的具体的实现 B
,根据配置文件,在创建 A
的时候,IOC 容器就知晓 A
依赖的 B
,这时候注入这个依赖即可。
如果之后你有新的实现需要替换,那 A
的代码不需要任何改动,你只需要将配置文件 A
依赖 B
改成 B1
,这样重启之后,IOC 容器会为 A
注入 B1
。
这样就使得类 A
和类 B
解耦了, very nice!
并且也因为创建对象由 IOC 全权把控,那么我们就能很方便的让 IOC 基于扩展点来“加工”对象,例如我们要代理一个对象,IOC 在对象创建完毕,直接判断下这个对象是否需要代理,如果要代理,则直接包装返回代理对象。
这等于我们只要告诉 IOC 我们要什么,IOC 就能基于我们提供的配置文件,创建符合我们需求的对象。
正是这个控制反转的思想,解放了我们的双手。
Spring IOC 容器的初始化过程
启动阶段:
配置加载:加载配置文件或配置类,IOC 容器首先需要加载应用程序的配置信息,这些配置信息可以是 XML 配置文件、Java 配置类或注解配置等方式。
创建容器:Spring 创建 IOC 容器(
BeanFactory
、ApplicationContext
),准备加载和管理 Bean。
Bean 定义注册阶段:
解析和注册:
BeanDefinitionReader
读取解析配置中的 Bean 定义,并将其注册到容器中,形成BeanDefinition
对象。
实例化和依赖注入:
实例化:根据
BeanDefinition
创建 Bean 的实例。依赖注入:根据
BeanDefinition
中的依赖关系,可以通过构造函数注入、Setter 注入或字段注入,将依赖注入到 Bean 中。
初始化:
BeanPostProcessor
处理:这些处理器会在 Bean 初始化生命周期中加入定义的处理逻辑,postProcessBeforeInitialization
和postProcessAfterInitialization
分别在 Bean 初始化前后被调用。Aware 接口调用:如果 Bean 实现了
Aware
接口(如BeanNameAware
、BeanFactoryAware
),Spring 会回调这些接口,传递容器相关信息。初始化方法调用:调用 Bean 的初始化方法(如通过
@PostConstruct
注解标注的方法,或实现InitializingBean
接口的 bean 会被调用afterPropertiesSet
方法)。