Spring IOC 核心原理
核心定位:搞懂 Spring IOC 容器的启动逻辑、Bean 的创建与管理流程,明确“Spring 底层如何干活”,是 Spring 所有特性的基础。
一、容器核心接口:BeanFactory vs ApplicationContext
核心结论:两者都是 IOC 容器核心接口,ApplicationContext 是 BeanFactory 的子接口,功能更完善,日常开发关注后者。
| 对比维度 | BeanFactory | ApplicationContext |
|---|---|---|
| 加载方式 | 懒加载(getBean 时才创建 Bean) | 预加载(容器启动时就创建所有 singleton Bean) |
| 功能范围 | 基础功能:Bean 的创建、获取、管理,无额外扩展 | 继承 BeanFactory 所有功能,新增:国际化、事件发布、资源加载、AOP 集成等 |
| 常见实现类 | XmlBeanFactory(已过时)、DefaultListableBeanFactory | ClassPathXmlApplicationContext、AnnotationConfigApplicationContext(最常用) |
| 关键点 | 体现 IOC 最基础的“控制反转”思想 | “懒加载 vs 预加载” |
二、IOC 容器启动流程 🔴
核心逻辑:加载配置 → 解析 Bean 定义 → 初始化容器 → 创建 Bean(预加载 singleton),简化时序如下(面试可直接口述):
-
加载配置资源:读取 XML、注解(@Component、@Configuration)、JavaConfig 等配置,获取 Bean 的定义信息(BeanDefinition)。
-
解析与注册 Bean:将配置中的 Bean 信息解析为 BeanDefinition 对象,注册到 BeanDefinitionRegistry(Bean 定义注册表)。
-
执行 BeanFactoryPostProcessor:对 BeanDefinition 进行修改(如修改 Bean 属性、新增 Bean),在 Bean 实例化之前执行。
-
初始化 Bean:根据 BeanDefinition 创建 Bean 实例(singleton 预加载,prototype 懒加载),执行依赖注入、初始化方法。
-
容器启动完成:Bean 已就绪,可通过容器获取 Bean 并使用。
重点:“配置加载→Bean定义注册→Bean初始化”三个核心步骤,BeanFactoryPostProcessor 的执行时机。
三、Bean 完整生命周期 🟠
核心流程:实例化 → 属性注入 → 初始化 → 销毁(4个核心阶段),补充完整执行顺序(结合扩展点):
-
实例化(Instantiation):通过构造器创建 Bean 实例(内存分配,此时 Bean 还未注入属性)。
-
属性注入(Populate):将容器中配置的依赖(@Autowired、XML 配置)注入到 Bean 实例中。
-
执行 Aware 接口方法:若 Bean 实现了 Aware 接口(如 BeanNameAware、BeanFactoryAware),容器会注入对应的资源(如 Bean 名称、BeanFactory)。
-
执行 BeanPostProcessor#postProcessBeforeInitialization:初始化前增强(所有 Bean 都会执行,可自定义扩展)。
-
初始化(Initialization):
-
执行 @PostConstruct 注解方法(JSR-250 规范);
-
执行 InitializingBean 接口的 afterPropertiesSet() 方法;
-
执行 XML 配置/JavaConfig 中定义的 init-method 方法。
-
-
执行 BeanPostProcessor#postProcessAfterInitialization:初始化后增强(如 AOP 动态代理,在这里生成代理对象)。
-
Bean 就绪:可正常使用(singleton 存于容器缓存,prototype 每次获取都新建)。
-
销毁(Destruction):
-
执行 @PreDestroy 注解方法;
-
执行 DisposableBean 接口的 destroy() 方法;
-
执行 XML 配置/JavaConfig 中定义的 destroy-method 方法;
-
容器关闭时,singleton Bean 会被销毁,prototype Bean 由开发者手动销毁。
-
流程:实例化 → 注属性 → Aware → 前置增强 → 初始化 → 后置增强 → 就绪 → 销毁
四、@Lazy 懒加载 & Bean 作用域
1. @Lazy 懒加载
-
作用:改变 singleton Bean 的加载时机,从“容器启动预加载”改为“第一次 getBean 时加载”。
-
适用场景:Bean 初始化耗时、占用资源多,且启动时不使用(如后台定时任务、非核心服务)。
-
注意:仅对 singleton 作用域有效,prototype 本身就是懒加载(每次获取都新建)。
2. Bean 作用域(5种,重点记前2种)
| 作用域 | 说明 | 适用场景 |
|---|---|---|
| singleton(默认) | 容器中仅存在一个 Bean 实例,所有请求共享,线程不安全(需注意全局变量) | 无状态 Bean(如 Service、Dao),日常开发90%以上场景 |
| prototype | 每次 getBean 都创建新实例,容器不管理销毁(开发者手动处理) | 有状态 Bean(如 Controller 中接收请求参数的对象、线程不安全的 Bean) |
| request | 每个 HTTP 请求创建一个实例,请求结束后销毁(仅 Web 环境有效) | Web 场景,存储请求级别的数据 |
| session | 每个 HTTP Session 创建一个实例,session 失效后销毁(仅 Web 环境有效) | Web 场景,存储会话级别的数据(如用户登录信息) |
| application | 整个 Web 应用生命周期内只有一个实例(与 singleton 区别:application 是 Web 容器级,singleton 是 Spring 容器级) | Web 应用全局共享的 Bean(如全局配置) |
五、依赖注入方式对比(最佳实践)
核心:Spring 支持3种依赖注入方式,重点“推荐用法”和“避坑点”。
| 注入方式 | 实现方式 | 优点 | 缺点 | 最佳实践 |
|---|---|---|---|---|
| 构造器注入 | @Autowired 标注构造器,或无注解(Spring 5.0+ 自动注入) | 强制依赖必须注入,避免空指针;Bean 实例化后即完整可用;支持 immutable 变量 | 依赖过多时,构造器参数过长,代码不简洁 | 推荐!依赖少(1-3个)时首选,适合强制依赖 |
| Setter 注入 | @Autowired 标注 Setter 方法 | 代码简洁;支持可选依赖(可设置默认值);灵活性高 | Bean 实例化后可能未注入依赖,存在空指针风险;不支持 immutable 变量 | 可选依赖时使用,配合 @Nullable 注解标注可选参数 |
| 字段注入 | @Autowired 直接标注字段(最简洁) | 代码极简,开发效率高 | 耦合度高;无法注入 final 字段;单元测试难以Mock;不支持依赖注入的清晰性 | 不推荐!仅临时测试、简单demo可用,生产环境避免使用 |
重点:说出“构造器注入首选”,并说明原因(强制依赖、避免空指针),同时区分三种方式的优缺点。
六、关键扩展点:BeanFactoryPostProcessor vs BeanPostProcessor
核心区别:执行时机不同、作用对象不同,是 Spring 扩展的核心。
| 对比维度 | BeanFactoryPostProcessor | BeanPostProcessor |
|---|---|---|
| 执行时机 | Bean 实例化 之前(仅执行一次,针对所有 BeanDefinition) | Bean 实例化、属性注入 之后,初始化前后(每个 Bean 都执行一次) |
| 作用对象 | BeanDefinition(Bean 的定义信息) | Bean 实例本身 |
| 核心作用 | 修改 BeanDefinition(如修改 Bean 属性、新增 Bean、替换 Bean 定义) | 对 Bean 实例进行增强(如修改 Bean 属性、生成代理对象) |
| 常见实现 | PropertyPlaceholderConfigurer(解析占位符 ${}) | AutowiredAnnotationBeanPostProcessor(处理 @Autowired 注入)、AOP 代理增强处理器 |
| 记忆技巧 | “改定义”:在 Bean 没创建前,修改它的“蓝图” | “改实例”:Bean 已经创建,对它进行加工增强 |
七、补充:FactoryBean 与 BeanFactory 区别 🟠
1. BeanFactory
-
本质:IOC 容器的核心接口,负责管理所有 Bean 的创建、获取、生命周期。
-
角色:“容器管理者”,管理的是所有普通 Bean。
2. FactoryBean
-
本质:一个 Bean(实现 FactoryBean 接口的 Bean),不是容器。
-
角色:“Bean 工厂”,负责创建特定的 Bean(如复杂对象、代理对象)。
-
核心方法:getObject() → 返回真正需要的 Bean 实例;getObjectType() → 返回 Bean 类型。
3. 核心区别
BeanFactory 是“容器”,管理所有 Bean;FactoryBean 是“Bean”,负责创建某一个特定的 Bean。
示例:Spring 中 AOP 的 ProxyFactoryBean、MyBatis 的 SqlSessionFactoryBean,都是 FactoryBean,用于创建复杂的代理对象/工厂对象。
八、补充:Aware 接口作用
-
核心作用:让 Bean 主动获取 Spring 容器中的资源(如 Bean 名称、BeanFactory、ApplicationContext),打破“控制反转”的封闭性。
-
常见 Aware 接口(记重点3个):
-
BeanNameAware:获取当前 Bean 的名称;
-
BeanFactoryAware:获取 BeanFactory 容器实例;
-
ApplicationContextAware:获取 ApplicationContext 容器实例(功能最全)。
-
-
注意:Aware 接口的方法由 Spring 容器自动调用,无需手动调用;仅当 Bean 实现该接口时,才会注入对应资源。
总结
-
BeanFactory 与 ApplicationContext 区别:懒加载 vs 预加载、功能多少;
-
IOC 容器启动流程:配置加载 → BeanDefinition 注册 → 初始化 Bean;
-
Bean 生命周期:实例化 → 属性注入 → 初始化 → 销毁(结合扩展点);
-
依赖注入首选构造器注入,避免字段注入;
-
BeanFactoryPostProcessor(改定义)与 BeanPostProcessor(改实例)的执行时机和作用。