Skip to content

依赖注入

  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> 按键获取。