Spring IOC(控制反转)容器的核心作用是管理 Bean 的生命周期,从加载配置、解析 Bean 定义,到创建、初始化 Bean,最终将可用的 Bean 提供给应用。整个加载流程可以分为容器初始化和Bean 实例化两大阶段,具体步骤如下(以最常用的ClassPathXmlApplicationContext
为例):
一、容器初始化阶段:加载并解析配置,注册 Bean 定义
这一阶段不创建 Bean 实例,只完成 “Bean 的元数据收集和注册”,相当于 “根据图纸准备好所有零件的清单”。
1. 定位资源(Resource)
容器首先要找到配置文件的位置(比如 XML 文件、注解类),这些配置描述了 “要创建哪些 Bean” 以及 “Bean 的依赖关系”。
- 例如:
new ClassPathXmlApplicationContext("spring.xml")
会从类路径下找到spring.xml
文件。 - 资源可以是本地文件、网络资源、类路径资源等,Spring 通过
Resource
接口统一处理。
2. 加载与解析 BeanDefinition
容器读取配置文件,将其中的 Bean 信息(如类名、属性、依赖、作用域等)解析成BeanDefinition 对象(Bean 的 “元数据”,相当于 “零件图纸”)。
- 解析 XML:比如
<bean id="userService" class="com.xxx.UserService">
会被解析成一个 BeanDefinition,记录id=userService
、class=UserService
等信息。 - 解析注解:比如
@Service
、@Component
注解的类,会被扫描并解析成 BeanDefinition。 - 解析内容:包括 Bean 的类名、作用域(单例 / 原型)、依赖的 Bean、初始化方法(
init-method
)、销毁方法(destroy-method
)等。
3. 注册 BeanDefinition
解析后的 BeanDefinition 会被注册到BeanDefinitionRegistry(Bean 定义注册表,本质是一个 Map)中,key 是 Bean 的名称(如userService
),value 是对应的 BeanDefinition。
- 此时容器中只有 “图纸”(BeanDefinition),还没有实际的 Bean 对象。
- 例如:注册后,容器知道 “有一个叫 userService 的 Bean,对应的类是 UserService,需要依赖 userDao”。
4. 调用 BeanFactoryPostProcessor(后置处理器)
这是容器初始化的最后一步,允许对已注册的 BeanDefinition 进行修改或增强(比如替换属性值、添加新定义等)。
- 典型例子:
PropertyPlaceholderConfigurer
会替换配置中的占位符(如${db.url}
替换成实际的数据库地址)。 - 执行完这一步,BeanDefinition 的信息就固定了,接下来将基于这些信息创建 Bean。
二、Bean 实例化阶段:创建并初始化 Bean
这一阶段根据注册的 BeanDefinition,真正创建 Bean 对象,完成依赖注入和初始化,相当于 “根据图纸造出零件并组装好”。
1. 实例化 Bean(创建对象)
容器根据 BeanDefinition 的信息,通过反射创建 Bean 的实例(调用类的构造方法)。
- 单例 Bean:默认在容器启动时就实例化(除非设置
lazy-init="true"
延迟加载)。 - 原型 Bean:每次调用
getBean()
时才实例化。 - 例如:对
UserService
的 BeanDefinition,容器会执行UserService service = new UserService()
创建实例。
2. 依赖注入(属性填充)
实例化后,容器会给 Bean 的属性赋值(包括依赖的其他 Bean),即 “依赖注入”(DI)。
- 例如:
UserService
依赖UserDao
,容器会从自己管理的 Bean 中找到userDao
实例,通过 setter 方法(setUserDao(userDao)
)或字段注入(@Autowired
)设置给UserService
。 - 这里会处理循环依赖(如 A 依赖 B,B 依赖 A),通过三级缓存提前暴露半成品 Bean 解决(详见之前的循环依赖讲解)。
3. 初始化 Bean(增强与就绪)
属性注入后,Bean 还需要经过一系列初始化操作才能 “可用”:
- 调用 Aware 接口:如果 Bean 实现了
BeanNameAware
、ApplicationContextAware
等接口,容器会传入对应的信息(如 Bean 的名称、ApplicationContext 对象)。 - 调用 BeanPostProcessor 前置方法:容器中的
BeanPostProcessor
(Bean 后置处理器)会对 Bean 进行前置处理(如 AOP 代理的准备工作)。 - 调用初始化方法:执行自定义的初始化逻辑,包括:
- 实现
InitializingBean
接口的afterPropertiesSet()
方法; - 配置中指定的
init-method
(如<bean init-method="init"/>
)。
- 实现
- 调用 BeanPostProcessor 后置方法:完成最终增强(如 AOP 动态代理就是在这里生成代理对象,替换原始 Bean)。
4. Bean 就绪,放入缓存
初始化完成后,单例 Bean会被放入一级缓存(singletonObjects
),供后续使用;原型 Bean则直接返回给调用者(不存入缓存)。
- 此时,通过
context.getBean("userService")
就能获取到完全可用的UserService
实例了。
三、容器关闭阶段:销毁 Bean
当容器关闭时(如调用close()
方法),会触发 Bean 的销毁流程:
- 调用
DisposableBean
接口的destroy()
方法; - 调用配置中指定的
destroy-method
(如<bean destroy-method="destroy"/>
)。
总结:Spring IOC 加载核心流程
- 找配置:定位资源(XML / 注解);
- 读配置:解析成 BeanDefinition(元数据);
- 记配置:注册 BeanDefinition 到注册表;
- 改配置:通过 BeanFactoryPostProcessor 修改定义;
- 造对象:实例化 Bean(反射创建);
- 填依赖:依赖注入(设置属性和依赖 Bean);
- 做初始化:执行 Aware 接口、初始化方法、后置处理器增强;
- 用 Bean:单例 Bean 入缓存,供应用使用;
- 清资源:容器关闭时销毁 Bean。
整个流程体现了 “控制反转” 的核心:应用不需要自己new
对象,而是由 IOC 容器统一创建、组装和管理,降低了代码耦合度。