Appearance
依赖注入
DI(Dependency Injection,依赖注入)是 IoC 的具体实现方式。 容器负责把 Bean 需要的依赖"注入"进去,而不是 Bean 自己去找。
一、三种注入方式
1.1 构造器注入(推荐)
java
@Service
public class OrderService {
private final OrderDao orderDao;
private final InventoryDao inventoryDao;
// 构造器注入:依赖在构造时传入,不可变
public OrderService(OrderDao orderDao, InventoryDao inventoryDao) {
this.orderDao = orderDao;
this.inventoryDao = inventoryDao;
}
}
// Spring 4.3+ 单构造器可以省略 @Autowired
// Lombok 简化:@RequiredArgsConstructor✅ 优点:
- 依赖不可变(final 字段)
- 保证依赖不为 null(构造时检查)
- 单元测试友好(直接 new 传参)
- 避免循环依赖(构造器循环依赖会报错,提前暴露问题)1.2 Setter 注入
java
@Service
public class OrderService {
private OrderDao orderDao;
@Autowired
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
}
// 适用场景:依赖可选或有默认值1.3 字段注入(不推荐)
java
@Service
public class OrderService {
@Autowired
private OrderDao orderDao; // 字段注入,最简洁但也最隐蔽
}
// 不推荐原因:
// 1. 依赖隐藏,不看代码不知道有哪些依赖
// 2. 无法用 final,依赖可变
// 3. 单元测试困难(需要反射或 Mockito 注入)二、@Autowired 注入规则
@Autowired 默认按类型注入:
@Autowired
private OrderDao orderDao;
→ 容器中查找类型为 OrderDao 的 Bean
→ 只有一个 → 直接注入
→ 多个同类型 → 按名称匹配(字段名 = Bean 名)
→ 还是找不到 → 报错
@Autowired + @Qualifier 按名称指定:
@Autowired
@Qualifier("orderDaoImpl")
private OrderDao orderDao;
→ 明确指定 Bean 名称
@Autowired(required = false) 可选注入:
@Autowired(required = false)
private CacheService cacheService;
→ 容器中没有该 Bean 也不报错,值为 null三、@Resource vs @Autowired
| 维度 | @Autowired | @Resource |
|---|---|---|
| 来源 | Spring 提供 | JDK 提供(JSR-250) |
| 注入规则 | 默认按类型 | 默认按名称 |
| 属性 | 只有 required | 有 name、type 等 |
| 推荐 | Spring 项目首选 | 需要按名称注入时 |
java
// @Resource 使用
@Resource(name = "orderDaoImpl") // 按名称注入
private OrderDao orderDao;
@Resource // 不指定 name,先按名称(字段名)再按类型
private OrderDao orderDao;四、依赖注入的底层实现
@Autowired 注入流程:
① Bean 实例化(通过构造器或工厂方法创建对象)
② 属性填充(populateBean)
├─ 遍历所有字段和方法
├─ 找到 @Autowired 注解的注入点
├─ 调用 DefaultListableBeanFactory.resolveDependency()
│ ├─ 按类型查找候选 Bean
│ ├─ 多个候选时按 @Primary → @Priority → 名称匹配
│ └─ 调用 getBean() 获取或创建依赖 Bean
└─ 反射设置字段值或调用 setter 方法
③ 初始化回调五、面试要点
Q1:构造器注入和字段注入怎么选?
构造器注入更推荐:依赖不可变(final)、保证不为 null、单元测试友好、避免循环依赖。字段注入虽然简洁,但隐藏依赖、不利于测试。
Q2:@Autowired 和 @Resource 的区别?
@Autowired 是 Spring 的,默认按类型注入;@Resource 是 JDK 的,默认按名称注入。Spring 项目用 @Autowired 即可。
Q3:@Autowired 注入一个接口,多个实现类怎么办?
1) @Primary 标记首选;2) @Qualifier 按名称指定;3) 注入 List<Interface> 获取所有实现;4) 注入 Map<String, Interface> 按键获取。
