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


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


    前言

    请看下面几个问题

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

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


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

    背景

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

    2. 我们点击右侧三个小点查看描述, 可以看到信息如下图
      在这里插入图片描述
      原因详情描述: 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. 属性注入
      可以看到, 我们开发最常用的就是属性注入

      @RestController
      public class AppointmentNumberConfigurationController {
      
          @Autowired
          private AppointmentNumberConfigurationService numberConfigurationService;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    2. set 方法注入
      set 方法注入也会用到@Autowired注解,但使用方式与属性注入有所不同,
      属性注入是用在成员变量上,而set 方法的时候,是用在成员变量的Setter函数上。

      @RestController
      public class AppointmentNumberConfigurationController {
      
      
          private AppointmentNumberConfigurationService numberConfigurationService;
      
          @Autowired
          public void setNumberConfigurationService(AppointmentNumberConfigurationService numberConfigurationService) {
              this.numberConfigurationService = numberConfigurationService;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    3. 构造方法注入
      Constructor Injection是构造器注入,是我们最为推荐的一种使用方式。
      但是, 每次注入都按照这样的流程去构造注入的话, 会显得比较麻烦.
      至于如何去简化这一步骤, 我们可以继续往下看.

      @RestController
      public class AppointmentNumberConfigurationController {
      
          final AppointmentNumberConfigurationService numberConfigurationService;
          
          public AppointmentNumberConfigurationController(AppointmentNumberConfigurationService numberConfigurationService) {
              this.numberConfigurationService = numberConfigurationService;
          }
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    4. 三种方式对比如下
      在这里插入图片描述

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

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

    解决

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

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

      @RestController
      @RequiredArgsConstructor
      public class AppointmentNumberConfigurationController {
          
          final AppointmentNumberConfigurationService numberConfigurationService;
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      同时需要注意:

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

          
              org.projectlombok
              lombok
              1.18.16
          
        
        • 1
        • 2
        • 3
        • 4
        • 5
      • 必须声明的变量为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的区别是什么?

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    线上教育知识付费小程序源码系统 界面支持DIY装修 带完整的安装代码包以及搭建部署教程
    自定义cell控件
    linux命令详解之-tar命令详解-归档及压缩
    2022-08-19 学习笔记 day42-JDBC
    Oracle数据库
    最具有中国特色的微服务组件,阿里新一代SpringCloud学习指南
    字符串匹配值Sunday算法
    不依赖第三方库,原生C代码进行BASE64编码解码
    Web(二)html5基础-文档头部(知识训练和编程训练)
    挑战杯 机器视觉人体跌倒检测系统 - opencv python
  • 原文地址:https://blog.csdn.net/m0_67402588/article/details/126080872