一、什么是依赖注入(DI)?

DI(Dependency Injection)直译为“依赖注入”,依赖注⼊是⼀个过程,是指IoC容器在创建Bean时,去提供运⾏时所依赖的资源,⽽资源指的就是对象

说定义可能过于抽象这边也举个例子,比如说你想喝奶茶了:

  • 没有 DI:你得自己买原料、煮茶、做奶盖(自己创建依赖)
  • 有 DI:你直接点外卖,奶茶店(IOC容器)做好奶茶(依赖)送到你手上(注入)

简单来说,就是把对象取出来放到某个类的属性中

DI的作用和价值

上述买奶茶的例子中,不需要你自己费心去制作奶茶,直接就有奶茶店给你制造好了,而相对应的意思就是,DI 通过让组件的依赖由外部提供,而非组件自身创建,实现组件与依赖解耦、提升代码可测试性并简化依赖维护

在⼀些⽂章中,依赖注⼊也被称之为"对象注⼊",“属性装配”,具体含义需要结合⽂章的上下⽂来理解

二、DI 的 3 种核心实现方式

关于依赖注⼊,Spring也给我们提供了三种⽅式:

  1. 属性注⼊(FieldInjection)
  2. 构造⽅法注⼊(ConstructorInjection)
  3. Setter 注⼊(SetterInjection)

1. 属性注⼊(FieldInjection)

直接在类的成员变量(属性)上标注注入注解@Autowired,由Spring框架自动为属性赋值

本章的代码演示我们都按照实际开发中的模式,将Service类注⼊到Controller类中

Service 类的实现代码如下:

在这里插入图片描述

Controller 类的实现代码如下:

在这里插入图片描述

获取Controller中的print⽅法:
在这里插入图片描述
测试结果:
在这里插入图片描述
去掉@Autowired后
在这里插入图片描述

你去掉了@Autowired,Spring就不会帮你把UserService的实例“填”到HelloController的userService属性里了,所以这个属性还是默认的null,调用null的print()方法就会报错

2. 构造⽅法注⼊(ConstructorInjection)

通过类的构造方法,将依赖作为参数传递给当前类,在对象创建的同时完成依赖赋值
Service 类的实现代码如下:
在这里插入图片描述

Controller 类的实现代码如下:
在这里插入图片描述

获取Controller中的print⽅法:
在这里插入图片描述

测试结果:
在这里插入图片描述

构造方法注入的其他情况

如果类只有⼀个构造⽅法,那么@Autowired 注解可以省略;如果类中有多个构造⽅法,那么需要添加上@Autowired 来明确指定到底使⽤哪个构造⽅法

情况代码演示

Service 类的实现代码如下:
在这里插入图片描述
在这里插入图片描述

Controller 类的实现代码如下:
在这里插入图片描述
测试结果:
在这里插入图片描述

3. Setter 注⼊(SetterInjection)

通过类的 setter 方法(即属性的 “设置方法”)传入依赖,Setter 注⼊和属性的Setter⽅法实现类似,只不过在设置set⽅法的时候需要加上@Autowired 注

Service 类的实现代码如下:
在这里插入图片描述
Controller 类的实现代码如下:
在这里插入图片描述
测试结果:
在这里插入图片描述

三、三种注⼊优缺点分析(了解即可)

  • 属性注入
    • 优点:简洁,使⽤⽅便
    • 缺点:
      • 只能⽤于IoC容器,如果是⾮IoC容器不可⽤,并且只有在使⽤的时候才会出现NPE(空指针异常)
      • 不能注⼊⼀个Final修饰的属性
  • 构造方法注入(Spring4.X推荐)
    • 优点:
      • 可以注⼊final修饰的属性
      • 注⼊的对象不会被修改
      • 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法
      • 通⽤性好,构造⽅法是JDK⽀持的,所以更换任何框架,他都是适⽤的
    • 缺点: 注⼊多个对象时,代码会⽐较繁琐
  • setter注入(Spring3.X推荐)
    • 优点:⽅便在类实例之后,重新对该对象进⾏配置或者注⼊
    • 缺点:
      • 不能注⼊⼀个Final修饰的属性
      • 注⼊对象可能会被改变,因为setter⽅法可能会被多次调⽤,就有被修改的⻛险

四、@Autowired存在问题

因为@Autowired是根据类型获取对象,当同⼀类型存在多个bean时,使用@Autowired会存在问题

代码演示

Controller 类的实现代码如下:
在这里插入图片描述

这里IDEA已经提醒出现错误了

Component 类的实现代码如下:
在这里插入图片描述

Student 类的实现代码如下:
在这里插入图片描述
测试结果:
在这里插入图片描述

解决@Autowired会存在问题的方法

上述报错的原因是,⾮唯⼀的Bean对象,为了解决这个问题,Spring提供了以下⼏三种解决⽅案:@Primary,@Qualifier,@Resource

1. @Primary

使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现
在这里插入图片描述

测试结果:
在这里插入图片描述

2. @Qualifier

按 Bean 名称精准锁定目标 Bean,但必须与 @Autowired搭配使用,可弥补 @Autowired 先按类型匹配的模糊性,实现类型筛选 + 名称精准匹配的注入逻辑
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
测试运行结果:
在这里插入图片描述

优先级

@Qualifier 优先高于@Primary,直接覆盖 @Primary 的默认选择

测试一下同时使用@Qualifier和@Primary
在这里插入图片描述
在这里插入图片描述
测试运行结果:
在这里插入图片描述
从上述测试结果可证明即使同时使用@Qualifier和@Primary,,@Qualifier优先级高于@Primary,直接将@Primary覆盖

3. @Resource

@Resource 是 JDK 原生的依赖注入注解,实现 Bean 的注入,其注入逻辑是也是先按名称匹配,再按类型匹配
在这里插入图片描述
在这里插入图片描述
测试运行结果:
在这里插入图片描述

注意,@Qualifier需要搭配@Autowired一起使用,@Resource不需要,且需要通过name属性指定要注⼊的bean的名称

五 、@Autowird与@Resource的区别

  • @Autowired是spring框架提供的注解,⽽@Resource是JDK提供的注解
  • @Autowired默认是按照类型注⼊,⽽@Resource是按照名称注⼊.相⽐于@Autowired 来说,@Resource ⽀持更多的参数设置,例如name设置,根据名称获取Bean

六 、最后说一下

结合上篇的IOC文章,我们可以对IOC是 Spring 的核心思想,DI是其具体实现手段这句话应该会有了更深的理解,也就是容器接管 Bean 的创建与生命周期,DI 负责依赖注入,二者共同实现组件解耦.
至此spring IOC和DI内容就告一段落

在这里插入图片描述

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐