Springframework系列 依赖注入
依赖注入概念
Spring官方解释
依赖注入(DI)是一个过程,对象仅通过构造参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后在其上设置的属性来定义它们的依赖(即与它们一起工作的其它对象)。然后,容器在创建 bean 时注入这些依赖。这个过程从根本上说是Bean本身通过使用类的直接构造或服务定位模式来控制其依赖的实例化或位置的逆过程(因此被称为控制反转)。
通俗易懂的话
当需要在一个对象中使用另一个不同对象时,需要将另一个对象进行实例化操作。
如:在方法中需要使用另一个对象
// 自己买菜、切菜、炒菜
class Chef {
public void cook() {
Vegetable veggie = new Vegetable(); // 强依赖
Pan pan = new Pan(); // 强依赖
pan.fry(veggie);
}
}
使用依赖注入方式
class Chef {
private Ingredient ingredient; // 食材由外卖员送来
private CookTool tool; // 厨具由外卖员送来
// 外卖员(Spring)自动送食材和厨具进来
public Chef(Ingredient ingredient, CookTool tool) {
this.ingredient = ingredient;
this.tool = tool;
}
public void cook() {
tool.fry(ingredient); // 直接用现成的
}
}
将目标对象注入到Spring容器
Springframework 提供了一系列注解支持将目标对象注入到Spring容器中。如:
- @Controller
- @Service
- @Repository
- @Component
等等。
为目标对象打上这些注解后,Spring自动会扫描对象中是否有:
- 构造函数
- setter方法
如果目标对象没有提供显示构造,则会使用Java提供的默认隐式构造。
依赖注入方式
- 通过构造器注入
public class Chef {
// 必需依赖(final 字段,不可变)
private final Ingredient ingredient;
private final CookTool tool;
// 构造器注入(Spring 会自动调用)
public Chef(Ingredient ingredient, CookTool tool) {
this.ingredient = ingredient;
this.tool = tool;
}
public void cook() {
tool.use(ingredient);
System.out.println("菜做好了!");
}
}
- 通过setter注入
public class Chef {
// 可选依赖(非 final 字段)
private Ingredient ingredient;
private CookTool tool;
// Setter 注入(Spring 会自动调用)
public void setIngredient(Ingredient ingredient) {
this.ingredient = ingredient;
}
public void setTool(CookTool tool) {
this.tool = tool;
}
public void cook() {
if (ingredient == null || tool == null) {
throw new IllegalStateException("缺少食材或厨具!");
}
tool.use(ingredient);
System.out.println("菜做好了!");
}
}
@Autowired与构造器注入
对比维度 | 构造器注入 | @Autowired 字段注入 |
---|---|---|
依赖不可变性 | ✅ 可用 final 字段 | ❌ 字段可变(可能被反射修改) |
线程安全 | ✅ 对象创建后依赖不可变 | ❌ 依赖可能被并发修改 |
单元测试友好度 | ✅ 直接 new MyService(mockRepo) | ❌ 需用反射或 Spring 容器 |
循环依赖检测 | ✅ 启动时立即发现 | ❌ 可能运行时才暴露问题 |
代码简洁性 | ❌ 需显式写构造器(可用 Lombok 简化) | ✅ 字段上加一行注解即可 |
Spring 推荐度 | ⭐⭐⭐⭐⭐(官方推荐) | ⭐⭐(历史遗留用法,不推荐新项目) |
适用场景 | 强依赖、核心服务 | 快速原型开发(但生产环境应避免) |
总结
用构造器依赖注入是最安全最稳健的,但是考虑到便捷性等因素,实际开发中可能会使用@Autowired、@Resource等更具便捷性的方式。