主要区别
写法1:字段注入(Field Injection)
java
@Autowired private PrintTemplateService templateService;
-
特点:Spring 自动注入依赖到字段
-
问题:
-
违反了单一职责原则(类可以有太多依赖)
-
不便于单元测试(必须通过反射设置私有字段)
-
可能导致循环依赖
-
无法声明字段为 final
-
写法2:构造器注入(Constructor Injection)
java
@RequiredArgsConstructor
public class PrintTemplateController {
private final PrintTemplateService templateService;
}
-
特点:使用 Lombok 生成包含所有 final 字段的构造器
-
Spring 会自动通过构造器注入依赖
-
优势:
-
依赖不可变(final)
-
便于单元测试(可以直接传参)
-
保证依赖完整性(对象创建时所有依赖就位)
-
避免循环依赖问题
-
符合不可变对象设计原则
-
推荐写法
强烈推荐写法2(构造器注入)
理由:
-
不可变性:final 字段确保依赖不会在运行时被修改
-
可测试性:可以直接在测试中 new 出对象
-
代码更安全:编译时就能发现依赖问题
-
Spring 官方推荐:Spring 官方自 4.3 版本起推荐构造器注入
最佳实践
java
@RestController
@RequestMapping("/print/template")
@Slf4j
@RequiredArgsConstructor
public class PrintTemplateController {
// 使用 final 确保不可变性
private final PrintTemplateService templateService;
private final TemplateRenderService renderService;
// 如果类比较简单,也可以显式写构造器(代替 @RequiredArgsConstructor)
// public PrintTemplateController(PrintTemplateService templateService,
// TemplateRenderService renderService) {
// this.templateService = templateService;
// this.renderService = renderService;
// }
}
特殊情况
如果必须使用字段注入(比如在某些框架的基类中),可以使用:
java
@Autowired(required = false) // 可选依赖 private SomeService someService;
但一般来说,构造器注入是首选方案,它能让代码更健壮、更易测试、更符合现代 Java 编程规范。
那为什么搞了@Autowired这个出来 ?
这是个很好的问题!@Autowired 的出现有其历史和技术背景:
历史演进
1. 早期 Spring(Spring 1.x-2.x)
主要使用 XML 配置:
xml
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>
这种方式繁琐且容易出错。
2. Spring 2.5(2007年)引入了 @Autowired
初衷是为了简化配置:
java
@Component
public class UserService {
@Autowired // 这当时是个巨大的进步!
private UserDao userDao;
}
在当时看来,这简直是"魔法":
-
告别冗长的 XML
-
代码更简洁
-
开发效率大幅提升
为什么还需要 @Autowired?
即使现在推荐构造器注入,@Autowired 仍有其价值:
1. 可选依赖
java
@Autowired(required = false) private OptionalDependency optionalDep;
2. Setter 注入
某些场景需要重新设置依赖:
java
private DataSource dataSource;
@Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
3. 复杂场景
-
多实现选择:
java
@Autowired
@Qualifier("primaryDataSource")
private DataSource dataSource;
-
集合注入:
java
@Autowired private List<Validator> validators; // 注入所有 Validator 实现
4. 第三方集成
有些老框架或第三方库必须使用字段注入:
java
// 某些 JPA 实现或旧框架 @PersistenceContext private EntityManager entityManager;
现代 Spring 的智能改进
Spring 4.3+(2016年)的改进
java
// 只有一个构造器时,可以省略 @Autowired
@Component
public class UserService {
private final UserDao userDao;
// 不需要 @Autowired!
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
Spring Boot 的进一步简化
配合 Lombok 更加简洁:
java
@Service
@RequiredArgsConstructor // Lombok 生成构造器
public class UserService {
private final UserDao userDao; // Spring 自动注入
}
技术对比表格
| 特性 | @Autowired 字段注入 | 构造器注入 |
|---|---|---|
| 出现时间 | 2007年(Spring 2.5) | 一直存在,2016年后推荐 |
| 当时定位 | 革命性简化方案 | 传统但可靠 |
| 当前定位 | 特定场景使用 | 默认推荐 |
| 不可变性 | ❌ 不支持 final | ✅ 支持 final |
| 可测试性 | 较差(需反射) | 优秀 |
| 循环依赖 | 可能隐藏问题 | 立即暴露问题 |
| 代码简洁度 | 非常简洁 | 较简洁(配合 Lombok) |
历史视角看这个问题
可以把依赖注入的发展看作一个螺旋上升的过程:
-
XML 配置时代(繁琐但明确)
-
@Autowired 时代(简化但引入新问题)
-
构造器注入复兴(取两者优点:简洁 + 健壮)
结论
@Autowired 在当时是革命性的进步,极大地提升了开发体验。但随着最佳实践的演进,我们发现构造器注入在大多数场景下是更好的选择。
现代 Spring 开发的建议:
-
✅ 首选构造器注入(配合 Lombok
@RequiredArgsConstructor) -
⚠️ 特定场景用 @Autowired(可选依赖、Setter 注入等)
-
❌ 避免随意使用字段注入
这就好比:电锯(@Autowired)让伐木更高效,但熟练后你会发现斧头(构造器注入)在某些场景更安全可控。工具本身没有绝对的好坏,关键看如何正确使用。
1084

被折叠的 条评论
为什么被折叠?



