为什么Spring和IDEA都不推荐使用 @Autowired 注解
一、前言
- Spring 为什么不推荐使用
@Autowired注解? - 为什么推荐使用
@Resource代替@Autowired注解? - 如何快速使用构造注入代替
@Autowired注解? @Autowired、@Resource、@Qualifier有何区别?
二、背景
idea中经常使用@Autowired注解,但会有一个警告Field injection is not recommended:不推荐使用属性注入。
三、原因
为什么 Spring 建议我们在Bean中使用构造注入呢? 想要回答这个问题, 我们需要了解 Spring的依赖注入(DI)方式
Spring常用的注入方式有: 简单类型注入, 集合类型注入, 域属性自动注入, 自动注入的类别, 空值注入, 构造注入 可以简化为: 属性注入, 构造方法注入, set 方法注入
1. 属性注入
@RestController
public class AppointmentNumberConfigurationController {
@Autowired
private AppointmentNumberConfigurationService numberConfigurationService;
}
2. set方法注入
@RestController
public class AppointmentNumberConfigurationController {
private AppointmentNumberConfigurationService numberConfigurationService;
@Autowired
public void setNumberConfigurationService(AppointmentNumberConfigurationService numberConfigurationService) {
this.numberConfigurationService = numberConfigurationService;
}
3. 构造方法注入
Constructor Injection是构造器注入,是我们最为推荐的一种使用方式。 但是, 每次注入都按照这样的流程去构造注入的话, 会显得比较麻烦.
@RestController
public class AppointmentNumberConfigurationController {
final AppointmentNumberConfigurationService numberConfigurationService;
public AppointmentNumberConfigurationController(AppointmentNumberConfigurationService numberConfigurationService) {
this.numberConfigurationService = numberConfigurationService;
}
}
四、方式对比
| 注入方式 | 可靠性 | 可维护性 | 可测试性 | 灵活性 | 循环关系的检测 | 性能 |
|---|---|---|---|---|---|---|
| Field Injection | 不可靠 | 低 | 差 | 很灵活 | 不检测 | 启动快 |
| Constructor Injection | 可靠 | 高 | 好 | 不灵活 | 自动检测 | 启动慢 |
| Setter Injection | 不可靠 | 低 | 好 | 很灵活 | 不检测 | 启动快 |
使用属性注入可能会出现的问题
基于属性注入的方式, 违背单一职责原则 因为现在的业务一般都会使用很多依赖, 但拥有太多的依赖通常意味着承担更多的责任,而这显然违背了单一职责原则. 并且类和依赖容器强耦合,不能在容器外使用。
基于属性注入的方式, 容易导致Spring 初始化失败 因为现在在Spring特别是Spring Boot使用中, 经常会因为初始化的时候, 由于属性在被注入前就引用而导致npe(空指针错误), 进而导致容器初始化失败(类似下面代码块). Java 在初始化一个类时, 是按照 静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 -> @Autowired 的顺序。 所以在执行这个类的构造方法时,person 对象尚未被注入,它的值还是 null。
通过@Autowired 注入, 又因为是 ByType 注入, 因此有可能会出现两个相同的类型bean 如下代码快, 就会产生两个相同的Bean, 进而导致Spring 装配失败
五、解决
如果一定要使用属性注入, 可以使用 @Resource 代替 @Autowired 注解
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
如果我们想使用按照名称byName来装配,可以结合@Qualifier注解一起使用。
如果可能的话, 尽量使用构造注入
Lombok提供了一个注解@RequiredArgsConstructor, 可以方便我们快速进行构造注入, 例如:
@RestController
@RequiredArgsConstructor
public class AppointmentNumberConfigurationController {
final AppointmentNumberConfigurationService numberConfigurationService;
}
同时需要注意:
使用@RequiredArgsConstructor注解需要导入Lombok 包 或者安装lombok 插件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
必须声明的变量为final
根据构造器注入的,相当于当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了, 其中每个参数代表一个对其他类的依赖。基于构造方法为属性赋值,容器通过调用类的构造方法将其进行依赖注入
六、思考
为什么推荐使用@Resource,不推荐使用@Autowired
通过对问题1 的梳理, 我们可以知道.
因为@Autowired 注解在Bean 注入的时候是基于ByType, 因此会由于注入两个相同类型的Bean导致装配失败
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。
如果我们想使用按照名称byName来装配,可以结合@Qualifier注解一起使用。