好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

为什么Spring和IDEA都不推荐使用 @Autowired 注解

前言

请看下面几个问题

Spring为什么不推荐使用 @Autowired  注解? 为什么推荐使用 @Resource  代替  @Autowired  注解? 如何快速使用构造注入代替  @Autowired  ? @Autowired, @Qualifier, @Resource,  三者有何区别?

下面, 我们带着以上问题去梳理和学习, 体会知识之间的关联性

Spring为什么不推荐使用@Autowired 注解

背景

做开发的同学可能都会发现, idea 在我们经常使用的 @Autowired 注解上添加了警告
警告内容是: Field injection is not recommended , 译为: 不推荐使用属性注入

我们点击右侧三个小点查看描述, 可以看到信息如下图

原因详情描述: Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".
译为: Spring 团队建议: 始终在您的 bean 中使用基于构造函数的依赖注入。始终对强制依赖项使用断言

原因

为什么 Spring 建议我们在Bean中使用构造注入呢?
想要回答这个问题, 我们需要了解 Spring的依赖注入(DI)方式
Spring常用的注入方式有: 简单类型注入, 集合类型注入, 域属性自动注入, 自动注入的类别, 空值注入, 构造注入
可以简化为: 属性注入, 构造方法注入, set 方法注入

下面, 来用代码展示下三种方式注入

属性注入
可以看到, 我们 开发最常用的就是属性注入

?

1

2

3

4

5

6

@RestController

public class AppointmentNumberConfigurationController {

 

     @Autowired

     private AppointmentNumberConfigurationService numberConfigurationService;

}

set 方法注入
set 方法注入也会用到 @Autowired 注解,但使用方式与属性注入有所不同,
属性注入是用在成员变量上,而set 方法的时候,是用在成员变量的Setter函数上。

?

1

2

3

4

5

6

7

8

9

10

@RestController

public class AppointmentNumberConfigurationController {

 

 

     private AppointmentNumberConfigurationService numberConfigurationService;

 

     @Autowired

     public void setNumberConfigurationService(AppointmentNumberConfigurationService numberConfigurationService) {

         this .numberConfigurationService = numberConfigurationService;

}

构造方法注入
Constructor Injection是构造器注入,是我们最为推荐的一种使用方式。
但是, 每次注入都按照这样的流程去构造注入的话, 会显得比较麻烦 .
至于如何去简化这一步骤, 我们可以继续往下看.

?

1

2

3

4

5

6

7

8

9

@RestController

public class AppointmentNumberConfigurationController {

 

     final AppointmentNumberConfigurationService numberConfigurationService;

    

     public AppointmentNumberConfigurationController(AppointmentNumberConfigurationService numberConfigurationService) {

         this .numberConfigurationService = numberConfigurationService;

     }

     }

三种方式对比如下

使用属性注入可能会出现的问题

基于属性注入的方式, 违背单一职责原则
因为现在的业务一般都会使用很多依赖, 但拥有太多的依赖通常意味着承担更多的责任,而这显然违背了单一职责原则.
并且类和依赖容器强耦合,不能在容器外使用。 基于属性注入的方式, 容易导致Spring 初始化失败
因为现在在Spring特别是Spring Boot使用中, 经常会因为初始化的时候, 由于属性在被注入前就引用而导致npe(空指针错误),
进而导致容器初始化失败(类似下面代码块). Java 在初始化一个类时,
是按照 静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 -> @Autowired 的顺序。
所以在执行这个类的构造方法时,person 对象尚未被注入,它的值还是 null。 通过@Autowired 注入, 又因为是 ByType 注入, 因此有可能会出现两个相同的类型bean
如下代码快, 就会产生两个相同的Bean, 进而导致Spring 装配失败

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

//2. 基于属性注入的方式, 容易导致Spring 初始化失败

@Autowired

private Person person;

 

private String company;

 

public UserServiceImpl(){

     this 测试数据pany = person.getCompany();

}

 

 

//3. 通过@Autowired 注入, 又因为是 ByType 注入, 因此有可能会出现两个相同的类型bean

public interface IUser {

     void say();

}

 

@Service

public class User1 implements IUser{

     @Override

     public void say() {

     }

}

 

@Service

public class User2 implements IUser{

     @Override

     public void say() {

     }

}

 

@Service

public class UserService {

 

     @Autowired

     private IUser user;

}

解决

如果一定要使用属性注入, 可以使用 @Resource 代替 @Autowired 注解
@Resource 的作用相当于 @Autowired ,只不过 @Autowired 按照 byType 自动注入。
如果我们想使用按照名称 byName 来装配,可以结合 @Qualifier 注解一起使用。

如果可能的话, 尽量使用构造注入
Lombok提供了一个注解 @RequiredArgsConstructor , 可以方便我们快速进行构造注入, 例如 :

?

1

2

3

4

5

6

@RestController

@RequiredArgsConstructor

public class AppointmentNumberConfigurationController {

    

     final AppointmentNumberConfigurationService numberConfigurationService;

     }

同时需要注意:

使用 @RequiredArgsConstructor 注解需要导入Lombok 包 或者安装lombok 插件

?

1

2

3

4

5

< 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 注解一起使用。

@Resource 装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
④如果既没有指定name,又没有指定type,则自动按照 byName 方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

因此, 如果一定要使用属性注入, 可以使用 @Resource 代替 @Autowired 注解

@Autowired, @Qualifier, @Resource, 三者有何区别

@Autowired : 通过 byType 方式进行装配, 找不到或是找到多个,都会抛出异常。 @Qualifier : 如果想让 @Autowired 注入的Bean进行 byName 装配, 可以使用 @Qualifier 进行指定 @Resource :作用相当于 @Autowired ,只不过 @Resource 默认按照 byName 方式装配, 如果没有匹配, 则退回到 byType 方式进行装配

参考文档

为什么IDEA不推荐你使用@Autowired?

@Autowired和@Resource的区别是什么?

到此这篇关于为什么Spring和IDEA都不推荐使用 @Autowired 注解的文章就介绍到这了,更多相关Spring IDEA @Autowired 注解内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://timepause.blog.csdn.net/article/details/123529701

查看更多关于为什么Spring和IDEA都不推荐使用 @Autowired 注解的详细内容...

  阅读:20次