Spring IoC 和依赖注入?

2025年 阅读约 15 分钟 面试指南 · Spring Boot

深入解析Spring IoC容器和依赖注入:控制反转思想、BeanFactory vs ApplicationContext、@Autowired vs @Resource、构造器注入 vs 字段注入、循环依赖三级缓存,附面试模拟问答。

一句话总结

IoC(控制反转):把对象创建和依赖管理的控制权从程序员交给 Spring 容器。程序员只需声明依赖关系,容器负责注入。DI(依赖注入)是 IoC 的实现方式,有三种:构造器注入(推荐,不可变、NPE 安全)、Setter 注入(可选依赖)、字段注入(@Autowired,简洁但不利于测试)。容器核心接口:BeanFactory(基础,懒加载)→ ApplicationContext(高级,支持国际化、事件、AOP)。

初级理解

什么是 IoC?

# 没有 IoC:自己 new 对象 public class UserService { private UserDao userDao = new UserDaoImpl(); // 硬编码 } # 有了 IoC:容器管理依赖 @Service public class UserService { @Autowired private UserDao userDao; // 容器注入 } # IoC 的好处 # 1. 解耦:UserService 不依赖具体实现 # 2. 易测试:可以注入 Mock 对象 # 3. 易扩展:切换实现只需改配置

三种注入方式

# 1. 构造器注入(推荐) @Service public class UserService { private final UserDao userDao; public UserService(UserDao userDao) { // 构造器 this.userDao = userDao; } } # 优点:不可变(final)、NPE 安全、利于测试 # 2. Setter 注入 @Service public class UserService { private UserDao userDao; @Autowired public void setUserDao(UserDao userDao) { this.userDao = userDao; } } # 适用:可选依赖 # 3. 字段注入(不推荐) @Service public class UserService { @Autowired private UserDao userDao; } # 缺点:不利于测试、隐藏依赖、可能 NPE

中级深入

@Autowired vs @Resource

对比维度@Autowired@Resource
来源Spring 提供JDK 提供(javax.annotation)
注入规则默认 byType,配合 @Qualifier byName默认 byName,找不到再 byType
required支持 required=false不支持
作用位置构造器、字段、方法、参数字段、方法
# @Autowired 配合 @Qualifier @Autowired @Qualifier("userDaoImpl") // 指定 bean name private UserDao userDao; # @Resource 指定 name @Resource(name = "userDaoImpl") private UserDao userDao;

BeanFactory vs ApplicationContext

对比维度BeanFactoryApplicationContext
加载方式懒加载(使用时才创建)预加载(启动时创建所有单例 Bean)
国际化不支持支持 MessageSource
事件发布不支持支持 ApplicationEvent
AOP需手动注册自动集成
使用场景资源受限环境常规应用(99% 场景)

高级拓展

循环依赖 — 三级缓存

# 三级缓存 # 一级:singletonObjects(完全初始化好的 Bean) # 二级:earlySingletonObjects(早期引用,未完成属性填充) # 三级:singletonFactories(Bean 工厂,可生成代理对象) # 解决流程(A 依赖 B,B 依赖 A) # 1. 创建 A → 放入三级缓存(singletonFactories) # 2. 填充 A 的属性 B → 发现 B 不存在 → 创建 B # 3. 创建 B → 放入三级缓存 # 4. 填充 B 的属性 A → 从三级缓存获取 A 的早期引用 → 注入 # 5. B 初始化完成 → 放入一级缓存 # 6. A 拿到 B → A 初始化完成 → 放入一级缓存 # 为什么需要三级缓存? # 二级缓存存的是原始对象 # 三级缓存存的是 ObjectFactory,可以返回代理对象 # 如果 A 需要 AOP 代理,三级缓存能保证 B 注入的是代理后的 A
注意:构造器注入无法解决循环依赖(因为构造器调用时对象还未创建),只有 Setter/字段注入可以。

实战场景

场景:多个实现类如何注入?

# 接口 + 多个实现 public interface PayService { void pay(); } @Service("alipay") public class AlipayService implements PayService { } @Service("wechat") public class WechatService implements PayService { } # 方式1:@Qualifier 指定 @Autowired @Qualifier("alipay") private PayService payService; # 方式2:@Primary 标记首选 @Service @Primary public class AlipayService implements PayService { } # 方式3:注入 Map/List @Autowired private Map<String, PayService> payServiceMap; // key = bean name, value = bean 实例 @Autowired private List<PayService> payServiceList; // 所有实现类

面试模拟

面试官:什么是 IoC?有什么好处?

你:IoC 即控制反转,把对象创建和依赖管理的控制权交给容器。好处:解耦(不依赖具体实现)、易测试(可注入 Mock)、易扩展(切换实现只需改配置)、统一管理(单例、生命周期)。

面试官:@Autowired 和 @Resource 有什么区别?

你:@Autowired 是 Spring 的,默认按类型注入,配合 @Qualifier 按名称;@Resource 是 JDK 的,默认按名称注入,找不到再按类型。@Autowired 支持 required=false,@Resource 不支持。