• 在简单了解IoC与DI中我们已经了解了DI的基本操作,接下来我们来详解DI。(IoC详解请看这里)
  • 我们已经知道DI是“你给我,我不用自己创建”的原则。现在我们来看看Spring是如何实现“给”这个动作的,也就是依赖注入的几种方式。

Spring主要提供了三种注入方式,都是通过 @Autowired 注解配合完成的:

  1. 属性注入 (Field Injection):直接在字段上使用 @Autowired
  2. 构造方法注入 (Constructor Injection):在类的构造方法上使用 @Autowired
  3. Setter 注入 (Setter Injection):在Setter方法上使用 @Autowired

三种注入方式

属性注入

方式:直接在需要注入的属性(字段)上加上 @Autowired

代码示例
首先,我们有一个 UserService Bean:

@Service // 告诉Spring:我是UserService,请你管理我
public class UserService {public void sayHi() {System.out.println("Hi, UserService");}
}

然后,在 UserController 中注入 UserService

@Controller
public class UserController {@Autowired // 告诉Spring:我需要一个UserService,请你直接注入到这个属性里private UserService userService;public void sayHi() {System.out.println("Hi, UserController...");userService.sayHi(); // 使用注入的UserService}
}

获取并使用

@SpringBootApplication
public class SpringTocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringTocDemoApplication.class, args);UserController userController = context.getBean(UserController.class);userController.sayHi();}
}

运行结果

Hi, UserController...
Hi, UserService

解释:Spring成功地将 UserService 实例注入到了 UserControlleruserService 属性中。

注意:如果你尝试在没有Spring容器的情况下直接调用 UserControllersayHi() 方法,userService 会是 null,导致空指针异常(NPE),因为Spring没有机会注入它。

构造方法注入

方式:在类的构造方法上加上 @Autowired。Spring会在创建这个类实例时,通过调用这个构造方法来注入依赖。

代码示例

@Controller
public class UserController2 {private UserService userService;@Autowired // 告诉Spring:请通过这个构造方法注入UserServicepublic UserController2(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("Hi, UserController2...");userService.sayHi();}
}

注意

  • 如果一个类只有一个构造方法,那么即使不加 @Autowired,Spring也会自动使用它进行注入(Spring 4.3+)。

[!TIP] 此时默认的无参构造方法不生效了,最好的习惯是补上默认的无参构造方法

  1. Spring默认的是使用无参的构造方法(如有多个构造方法)
  • 这样会导致之后在使用到userService的地方有空指针报错异常(可以通过打印日志的方法来确定具体使用哪一个方法)
  • 所以有多个构造方法的情况下,需要在需要的构造方法上加上 @Autowired注解表示指定使用哪一个

Setter 注入

方式:在Setter方法上加上 @Autowired。Spring会先创建对象,然后调用对应的Setter方法来注入依赖。

代码示例

@Controller
public class UserController3 {private UserService userService;@Autowired // 告诉Spring:请通过这个Setter方法注入UserServicepublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("Hi, UserController3...");userService.sayHi();}
}

[!QUESTION] 如果 setUserService 方法上没有 @Autowired,Spring还能注入吗?
不能。Spring需要 @Autowired 来知道这个方法是用来注入依赖的。

三种注入方式优缺点分析

#面经

方式优点缺点
属性注入最简洁,使用方便。1. 违反单一职责原则(SRP)。
2. 难以测试(只能用于IoC容器,并且在使用的时候才会出现空指针异常)。
3. 无法声明为 final
构造方法注入1. 依赖明确,强制依赖。
2. 可以声明为 final(推荐)。
3. 保证对象完全初始化。因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
4. 更好的可测试性。
5. 通用性好,因为构造方法是JDK支持的,所以换任何框架都是使用的。
1. 依赖过多时,构造方法参数列表会很长。
Setter 注入1. 依赖可选(不强制)。
2. 可以在对象创建后进行配置。
1. 无法声明为 final
2. 可能会被多次调用,存在风险。

推荐:在大多数情况下,构造方法注入是最佳实践。它确保了依赖的不可变性,并使对象在创建时就处于完全可用的状态。

@Autowired 存在问题?

问题:如果Spring容器中有多个相同类型的Bean,@Autowired 会怎么做?

代码示例
我们定义了两个 User 类型的Bean,名字分别是 user1user2

@Configuration
public class BeanConfig {@Bean("user1")public User user1() { /* ... */ }@Bean("user2")public User user2() { /* ... */ }
}

现在,我们在 UserController 中尝试注入 User

@Controller
public class UserController {@Autowiredprivate UserService userService; // 假设只有一个UserService@Autowiredprivate User user; // 尝试注入Userpublic void sayHi() {System.out.println("Hi, UserController...");userService.sayHi();System.out.println(user); // 打印注入的User}
}

运行结果

// 报错:NoUniqueBeanDefinitionException: No qualifying bean of type 'User' available: expected single matching bean but found 2

解释:Spring发现有两个 User 类型的Bean(user1user2),它不知道该注入哪一个,所以报错了。

如何解决“多个相同类型Bean”的问题?

Spring提供了几种方式来解决歧义:

  1. @Primary:优先注入。
  2. @Qualifier:指定Bean的名称。
    • @Qualifier 优先级比 @Primary
  3. @Resource:按名称注入(JDK标准)。
@Primary

作用:当有多个相同类型的Bean时,给其中一个加上 @Primary,表示它是首选的。

代码示例

@Configuration
public class BeanConfig {@Bean("user1")@Primary // 告诉Spring:当需要User类型时,优先注入我public User user1() { /* ... */ }@Bean("user2")public User user2() { /* ... */ }
}

现在,UserController 再次注入 User

@Controller
public class UserController {@Autowiredprivate User user; // 会自动注入user1// ...
}

解释:当Spring需要注入 User 类型时,它会优先选择带有 @Primaryuser1

@Qualifier

作用:明确指定要注入哪个Bean的名称。

代码示例

@Controller
public class UserController {@Autowired@Qualifier("user2") // 告诉Spring:我要注入名为“user2”的User Beanprivate User user;// ...
}

解释@Qualifier 必须和 @Autowired 一起使用。它告诉Spring,即使有多个 User 类型的Bean,我只想要那个名字是 user2 的。

[!IMPORTANT] @Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

@Resource

作用@Resource 是JDK提供的注解,它默认是按名称进行注入。如果找不到同名的Bean,再尝试按类型注入。

代码示例

@Controller
public class UserController {@Resource(name = "user1") // 告诉Spring:我要注入名为“user1”的Beanprivate User user;// ...
}

[[四种不同情况反应构造Spring框架中Bean的依赖注入和自动装配的不同规则]]

#面经
@Autowired vs @Resource 的区别:

  1. @Autowired是spring提供的注解,@Resource是JDK提供的注解
  • @Autowired:Spring特有的,默认按类型注入。如果类型有多个,再尝试按名称匹配。
  • @Resource:JDK标准,默认按名称注入。如果名称找不到,再尝试按类型匹配,并且其支持更多的参数设置,如name:根据名称获取Bean

Autowired装配顺序

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88419.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88419.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88419.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Arcgis连接HGDB报错

文章目录环境症状问题原因解决方案环境 系统平台:Linux x86-64 Red Hat Enterprise Linux 7 版本:6.0 症状 Arcgis连接HGDB报错: 无法连接到数据库服务器来检索数据库列表;请检查服务器名称、用户名和密码信息,然后…

Android 应用常见安全问题

背景:OWASP MASVS(Mobile Application Security Verification Standard 移动应用安全验证标准)是移动应用安全的行业标准。 一、MASVS-STORAGE:存储 1.1 不当暴露FileProvider目录 配置不当的 FileProvider 会无意中将文件和目录暴露给攻击者…

Netty的内存池机制怎样设计的?

大家好,我是锋哥。今天分享关于【Netty的内存池机制怎样设计的?】面试题。希望对大家有帮助; Netty的内存池机制怎样设计的? 超硬核AI学习资料,现在永久免费了! Netty的内存池机制是为了提高高并发环境下的内存分配与回收效率…

Python 项目快速部署到 Linux 服务器基础教程

Linux的开源特性和强大的命令行工具使得部署流程高度自动化,可重复性强。本文将详细介绍如何从零开始快速部署Python项目到Linux服务器。 Linux系统因其稳定性、安全性和性能优化,成为Python项目部署的首选平台。无论是使用flask构建Web应用、FastAPI创…

SQL Server通过CLR连接InfluxDB实现异构数据关联查询技术指南

一、背景与需求场景 在工业物联网和金融监控场景中,实时时序数据(InfluxDB)需与业务元数据(SQL Server)联合分析: 工业场景:设备传感器每秒采集温度、振动数据(InfluxDB),需关联工单状态、设备型号(SQL Server)金融场景:交易流水时序数据(每秒万条)需实时匹配客…

机器学习详解

## 深入解析机器学习:核心概念、方法与未来趋势机器学习(Machine Learning, ML)作为人工智能的核心分支,正深刻重塑着我们的世界。本文将系统介绍机器学习的基本概念、主要方法、实际应用及未来挑战,为您提供全面的技术…

汽车间接式网络管理的概念

在汽车网络管理中,直接式和间接式管理是两种用于协调车载电子控制单元(ECUs)之间通信与行为的机制。它们主要用于实现车辆内部不同节点之间的协同工作,特别是在涉及网络唤醒、休眠、状态同步等场景中。### 直接式管理直接式网络管…

npm : 无法加载文件 D:\Node\npm.ps1,因为在此系统上禁止运行脚本。

npm : 无法加载文件 D:\Node\npm.ps1,因为在此系统上禁止运行脚本。 安装高版本的node.js,可能会导致这个问题, 脚本的权限被限制了,需要你设置用户权限。 get-ExecutionPolicy set-ExecutionPolicy -Scope CurrentUser remotesig…

搜索算法讲解

搜索算法讲解 深度优先搜索-DFS P1219 [USACO1.5] 八皇后 Checker Challenge 一个如下的 666 \times 666 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线&#xff…

深度学习---Rnn-文本分类

# 导入PyTorch核心库 import torch # 导入神经网络模块 import torch.nn as nn # 导入优化器模块 import torch.optim as optim # 导入函数式API模块 import torch.nn.functional as F # 导入数据集和数据加载器 from torch.utils.data import Dataset, DataLoader # 导入NumPy…

20250709解决KickPi的K7开发板rk3576-android14.0-20250217.tar.gz编译之后刷机启动不了

【整体替换】 Z:\20250704\rk3576-android14.0\rkbin清理编译的临时结果: rootrootrootroot-X99-Turbo:~$ cd 14TB/versions/rk3576-android14.0-20250217k7/ rootrootrootroot-X99-Turbo:~/14TB/versions/rk3576-android14.0-20250217k7$ ll rootrootrootroot-X99-…

怎么创建新的vue项目

首先,新建一个文件点文件路径,输入cmd

CIU32L051系列 DMA串口无阻塞性收发的实现

1.CIU32L051 DMA的通道映射由于华大CIU32L051的DMA外设资源有限,DMA只有两个通道可供使用,对应的通道映射图如下:2.UART对应的引脚分布及其复用映射CIU32L051对应的UART对应的引脚映射图如下,这里博主为了各位方便查找,就直接全拿…

飞算 JavaAI 体验:重塑 Java 开发的智能新范式

飞算 JavaAI 体验:重塑 Java 开发的智能新范式引言:正文:一、工程化代码生成:从 "片段拼接" 到 "模块交付"1.1 传统工具的局限与突破1.2 代码质量验证二、智能重构引擎:从 "问题修复" 到…

深入理解JVM的垃圾收集(GC)机制

引言首先我们来介绍垃圾收集的概念,什么是垃圾收集?垃圾收集 (Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存爆掉。有效的使用可以使用的内存,对内存堆中已经死亡…

【笔记】国标-机动车辆及挂车分类

源于:GB/T 15089-2001机动车辆及挂车分类 1.L类:两轮或三轮车辆2.M类:四轮载客车辆3.N类:四轮载货车辆4.O类:挂车5.G类:其他

VLLM部署DeepSeek-LLM-7B-Chat 模型

一、部署环境准备1. 基础环境要求操作系统:Linux(推荐欧拉系统、Ubuntu 等)Python 版本:3.8 及以上依赖工具:pip、git、curl可选依赖:GPU 环境:NVIDIA GPU(支持 CUDA 11.7&#xff0…

翱翔的智慧之翼:Deepoc具身智能如何赋能巡检无人机“读懂”工业现场

翱翔的智慧之翼:Deepoc具身智能如何赋能巡检无人机“读懂”工业现场在百米高的风力发电机叶片顶端,在蜿蜒数十公里的高压输电线旁,在油气管道穿越的崇山峻岭之上,一架四旋翼无人机正精准地悬停着,它的“眼睛”&#xf…

Java大厂面试实录:谢飞机的电商场景技术问答(Spring Cloud、MyBatis、Redis、Kafka、AI等)

Java大厂面试实录:谢飞机的电商场景技术问答(Spring Cloud、MyBatis、Redis、Kafka、AI等)本文模拟知名互联网大厂Java后端岗位面试流程,以电商业务为主线,由严肃面试官与“水货”程序员谢飞机展开有趣的对话&#xff…

Kotlin基础

前言 Decrement(递减) → 将一个值减 1 的操作 Predicate(谓词) → 返回布尔值(逻辑值)的函数 Reference(引用) → 允许使用自定义名称与对象交互 Runtime(运行时&…