通俗易懂!Spring 依赖注入(DI)三种方式一次讲透(2025 面试最爱问,背完直接画图秒杀)
一句话记住:
依赖注入就是“我要用别人,不自己 new”,Spring 帮你自动塞进来。
三种方式:构造器注入(最推荐)、Setter 注入(老古董)、注解注入(日常主力 @Autowired)
三种方式对比表(面试必画,一目了然)
| 注入方式 | 配置方式 | 优点 | 缺点 | 2025 项目推荐度 | 适用场景 |
|---|---|---|---|---|---|
| 构造器注入 | 或 自动(推荐) | 强制依赖、不可变、便于测试、支持循环依赖 | 参数多时构造器很长 | ★★★★★(大厂标配) | 所有必填依赖(Service、Repository) |
| Setter 注入 | 或 @Autowired 于 set 方法 | 可选依赖、灵活改值 | 可被外部改成 null、不安全 | ★★ | 可选依赖、老项目维护 |
| 注解注入 | @Autowired(最常用) | 代码最少、最爽、支持字段/Setter/构造器 | 容易乱用、可被反射改值 | ★★★★ | 日常开发 90% 场景 |
1. 构造器注入(2025 最推荐!大厂强制)
为什么最牛?
- 依赖不可变(final)
- 对象一出生就完整(避免半初始化)
- 单元测试超简单(直接 new)
- Spring Boot 2.6+ 默认优先构造器注入
代码示例(超级简单):
@Service
public class UserService {
private final UserRepository repository; // final 强制注入
// Spring 自动注入(只有一个构造器时,@Autowired 可省略!)
public UserService(UserRepository repository) {
this.repository = repository;
}
public void save() {
repository.save();
}
}
老 XML 方式(了解即可):
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepository"/>
</bean>
2. Setter 注入(基本没人用了)
@Service
public class OrderService {
private PaymentService paymentService;
@Autowired // 可以加在 set 方法上
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
老 XML:
<bean id="orderService" class="com.example.OrderService">
<property name="paymentService" ref="paymentService"/>
</bean>
3. 注解注入(你天天在用,90% 代码都是它)
三种写法都行(推荐字段注入,代码最少):
@Service
public class ProductService {
@Autowired // 最常见!直接写在字段上
private CategoryService categoryService;
// 或者写在 Setter 上(和上面一样效果)
// @Autowired
// public void setCategoryService(CategoryService categoryService) { ... }
// 或者写在构造器(最推荐,但代码稍多)
// private final CategoryService categoryService;
// @Autowired
// public ProductService(CategoryService categoryService) { ... }
}
其他常用注解:
- @Resource(JDK 自带,按 name 注入)
- @Inject(JSR-330 标准,和 @Autowired 差不多)
- @Value(“${app.name}”) 注入配置值
- @Qualifier(“xxx”) 解决同类型多个 Bean 冲突
实际项目 2025 真实用法(直接抄)
@RestController
public class UserController {
private final UserService userService; // 构造器注入(推荐)
public UserController(UserService userService) {
this.userService = userService;
}
@Autowired
private RedisTemplate<String, Object> redis; // 字段注入(简单场景)
@GetMapping("/list")
public List<User> list() {
return userService.findAll();
}
}
面试最爱问的 6 个问题 + 标准答案
- 三种注入方式的区别?
答:构造器最安全强制、Setter 最灵活可选、注解最简洁。推荐构造器 + @Autowired。 - 为什么大厂都用构造器注入?
答:依赖不可变、支持 final、避免空指针、便于测试、不支持循环依赖时能提前报错。 - @Autowired 可以省略吗?
答:可以!只有一个构造器时,Spring 自动注入,@Autowired 可不写(Boot 2.6+ 默认)。 - 多个相同类型 Bean 怎么选?
答:用 @Qualifier(“beanName”) 或 @Primary 标记主候选。 - 循环依赖用哪种方式会报错?
答:构造器注入会直接报错(无法解决),字段/Setters 能用三级缓存解决。 - @Autowired 是怎么实现的?
答:AutowiredAnnotationBeanPostProcessor 在 Bean 生命周期的属性填充阶段完成注入。
终极记忆口诀(10 秒背会)
“构造器最牛强制填,Setter 老古董可选,
注解最爽天天见,字段构造随便选,
大厂只爱构造器,代码干净又安全!”
一张图记住(面试 30 秒画完)
构造器注入(推荐★★★★★)
↓
注解注入(日常★★★★)
↓
Setter 注入(过时★★)
背完这张表 + 示例代码 + 口诀,
面试官问“Spring 依赖注入有几种方式”,你 1 分钟画表 + 甩代码,直接满分!
现在你已经彻底掌握 DI 了,下一个目标:自己写一个循环依赖案例试试!冲!