《Spring 依赖注入方式全解析》
一、Spring 依赖注入概述
Spring 依赖注入(Dependency Injection,DI)是一种重要的设计模式,它在 Spring 框架中扮演着关键角色。依赖注入的核心概念是将对象所需的依赖关系由外部容器(通常是 Spring 容器)进行管理和注入,而不是让对象自己去创建和管理依赖。
这种方式具有极大的重要性。首先,它实现了解耦。在传统的编程方式中,对象之间的依赖关系通常是在对象内部通过直接实例化来建立的,这会导致对象之间高度耦合,难以维护和扩展。而依赖注入使得对象只关注自身的核心业务逻辑,不需要关心依赖对象的创建和获取方式,从而降低了对象之间的耦合度。
例如,在一个企业级应用中,一个业务服务类可能依赖于数据访问层的某个 DAO(Data Access Object)。如果采用传统方式,业务服务类需要自己实例化 DAO 对象,这样一旦 DAO 的实现发生变化,业务服务类也需要相应修改。但通过依赖注入,业务服务类只需要声明对 DAO 的依赖,由 Spring 容器在运行时将合适的 DAO 实例注入到业务服务类中,大大提高了代码的可维护性。
此外,依赖注入还提高了代码的可测试性。在单元测试中,可以轻松地替换依赖对象,模拟不同的场景,而不需要实际创建复杂的依赖关系。
总之,Spring 依赖注入通过将对象的依赖关系外部化,实现了解耦和可维护性,是 Spring 框架中不可或缺的一部分。
二、常见的依赖注入方式
(一)属性注入
属性注入是一种常见的依赖注入方式,它通过 set 方法注入 Bean 的属性值或依赖对象。这种方式具有很高的灵活性,因为可以在对象实例化后根据需要动态地设置属性值。
例如,在一个 Java 项目中,有一个名为UserService的服务类,它依赖于一个UserRepository接口的实现类来进行用户数据的操作。如果使用属性注入,可以在UserService类中定义一个UserRepository类型的属性,并提供对应的 set 方法。在 Spring 配置文件中,可以通过<property>标签将具体的UserRepository实现类注入到UserService中。
属性注入的优点在于灵活性高,可以根据不同的情况在运行时动态地设置属性值。同时,对于一些可选的依赖关系,也可以在需要的时候进行注入,而不是在对象实例化时强制注入。
(二)构造函数注入
构造函数注入是在对象实例化时,通过构造函数设置必要的属性。这种方式确保了对象实例化后即可使用,因为所有必要的依赖都在对象创建时被注入。
以一个学生管理系统为例,有一个Student类,它有name、age和grade等属性。如果使用构造函数注入,可以在Student类的构造函数中接收这些属性的值,并在对象创建时进行初始化。在 Spring 配置文件中,可以使用<constructor-arg>标签来指定构造函数的参数值。
构造函数注入的优点在于可以确保对象在创建时就处于一个完整的状态,避免了在使用对象之前可能出现的未初始化状态。同时,它也使得对象的依赖关系更加明确,因为在构造函数中可以清楚地看到对象所依赖的所有资源。
(三)Setter 方法注入
Setter 方法注入是在对象实例化后,通过调用 setter 方法实现依赖注入。这种方式使得依赖关系成为可选的,因为可以在需要的时候才进行注入。
比如在一个电商系统中,有一个Order类,它依赖于一个PaymentService类来处理支付操作。如果使用 Setter 方法注入,可以在Order类中定义一个PaymentService类型的属性,并提供对应的 setter 方法。在需要进行支付操作时,可以通过 Spring 容器调用 setter 方法将PaymentService实例注入到Order对象中。
Setter 方法注入的优点在于灵活性高,可以根据不同的业务场景在运行时动态地注入依赖关系。同时,对于一些可选的依赖,也可以在需要的时候进行注入,而不会在对象实例化时强制注入不必要的依赖。
三、其他依赖注入方式
(一)基于注解的自动装配
@Autowired注解是 Spring 框架中用于自动装配的重要注解之一。它可以应用于构造器、字段和方法注入。
在构造器注入中,当@Autowired注解用于构造器时,Spring 会在创建 Bean 实例时自动调用该构造器,并为其参数注入对应类型的实例。例如:
@Servicepublic class DriverServiceImpl implements DriverService {private DriverDao driverDao;@Autowiredpublic DriverServiceImpl(DriverDao driverDao) {this.driverDao = driverDao;}}
在接口注入中,@Autowired可以用于接口的实现类,自动注入实现接口的具体对象。
在方法注入中,如果方法有参数,会使用@Autowired的方式在容器中查找是否有该参数,并执行该方法。比如:
@Autowiredpublic void commonMethod(Bean04 bean04){System.out.println("普通方法的执行");}
@Autowired默认按类型注入。这意味着 Spring 容器会自动查找与所需类型匹配的 Bean 进行注入。此时,要求 Spring 容器中有且仅有一个合适的 Bean 为其赋值。但如果项目中有多个 Bean 可以赋值,则会发生错误。可以通过结合@Qualifier注解来指定具体的 Bean 名称进行注入,避免这种错误。
(二)Resource 注入
@Resource注解是 Java 标准(JSR-250)提供的注解,Spring 也支持该注解。它主要有name和type两个重要属性。
如果同时指定了name和type,则从 Spring 上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。例如:
@Resource(name = "myBean", type = MyBean.class)private MyBean myBean;
如果指定了name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常。
如果指定了type,则从上下文中找到类型匹配的唯一 bean 进行装配,找不到或者找到多个,都会抛出异常。
如果既没有指定name,又没有指定type,则自动按照 byName 方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
(三)接口注入
接口注入是一种通过接口来实现依赖注入的方式。在接口中定义要注入的信息,然后通过实现该接口的类来完成注入。
例如:
public class ClassA {private InterfaceB clzB;public init() {Object obj = Class.forName(Config.BImplementation).newInstance();clzB = (InterfaceB)obj;}}
在这种方式中,通过接口将调用者与实现者分离,提高了代码的可维护性和可扩展性。但接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。
四、不同注入方式对比
(一)可靠性
- 属性注入:不可靠。属性注入是在对象实例化后通过 set 方法进行注入,这意味着在对象使用过程中,属性可能会被意外修改,导致对象状态不可预测。
- 构造函数注入:可靠。构造函数注入在对象实例化时就将所有必要的依赖注入,一旦对象创建完成,其依赖关系就不会再发生变化,保证了对象的稳定性和可靠性。
- Setter 方法注入:不可靠。虽然 Setter 方法注入可以在对象实例化后进行依赖注入,但这也使得对象的依赖关系可以在运行时被随意修改,增加了对象状态的不确定性。
(二)可维护性
- 属性注入:差。属性注入的依赖关系不明显,难以直接从代码中看出对象的依赖关系,不利于代码的维护和理解。
- 构造函数注入:好。构造函数中明确列出了对象所依赖的资源,使得依赖关系一目了然,方便开发者进行代码维护和分析。
- Setter 方法注入:差。Setter 方法注入的依赖关系也不够直观,需要通过查看 setter 方法才能确定对象的依赖关系,增加了维护的难度。
(三)可测试性
- 属性注入:差。在进行单元测试时,由于属性注入的对象可能会受到外部环境的影响,难以进行有效的模拟和控制,导致测试难度较大。
- 构造函数注入:好。构造函数注入使得对象的依赖关系在创建时就确定,在单元测试中可以方便地通过构造函数传入模拟的依赖对象,进行测试。
- Setter 方法注入:好。Setter 方法注入可以在测试时根据需要设置不同的依赖对象,方便进行各种场景的测试。
(四)灵活性
- 属性注入:很灵活。属性注入可以在对象实例化后根据需要动态地设置属性值,对于一些可选的依赖关系非常方便。但这种灵活性也可能导致代码的混乱和不可控。
- 构造函数注入:不灵活。构造函数注入在对象创建时就确定了依赖关系,不能在运行时进行修改,缺乏一定的灵活性。
- Setter 方法注入:很灵活。Setter 方法注入可以在对象实例化后根据业务需求动态地注入依赖关系,具有较高的灵活性。但也可能导致依赖关系的不明确和代码的复杂性。
(五)循环关系检测
- 属性注入:不检测。属性注入方式不会自动检测 Bean 之间的循环依赖关系,可能会导致应用程序出现问题而难以排查。
- 构造函数注入:自动检测。构造函数注入在对象创建时会自动检测循环依赖关系,如果存在循环依赖,会抛出异常,便于开发者及时发现和解决问题。
- Setter 方法注入:不检测。Setter 方法注入也不会自动检测循环依赖关系,可能会导致应用程序出现死锁等问题。
(六)性能表现
- 属性注入:启动快。属性注入在启动时不需要进行复杂的依赖关系处理,启动速度相对较快。
- 构造函数注入:启动慢。构造函数注入需要在对象创建时处理所有的依赖关系,这可能会导致启动时间延长。
- Setter 方法注入:启动快。Setter 方法注入在对象实例化时不需要处理依赖关系,可以在需要时进行注入,启动速度相对较快。
五、Spring 官方推荐及原因
Spring 官方推荐构造器注入,这一推荐有多个重要原因。
首先,IDEA 警告提示 “Field injection is not recommended”,即不建议使用属性注入(字段注入)。这是因为属性注入存在一些弊端。属性注入是通过在类的变量上使用注解进行依赖注入,本质上是通过反射的方式直接注入到字段。虽然这种方式非常简洁,代码看起来简单易懂,类可以专注于业务而不被依赖注入所污染,只需要把注解扔到变量之上就好,不需要特殊的构造器或者 set 方法,依赖注入容器会提供所需的依赖。但是,成也萧何败也萧何,属性注入也会引发很多问题。
一方面,容易违背单一职责原则。使用属性注入方式,添加依赖很简单,普通开发者很可能会无意识地给一个类添加很多依赖,而当使用构造器方式注入,到了某个特定的点,构造器中的参数变得太多以至于很明显地发现 something is wrong。拥有太多的依赖通常意味着类要承担更多的责任,明显违背了单一职责原则。
另一方面,属性注入会导致依赖注入与容器本身耦合。具体表现为类和依赖容器强耦合,不能在容器外使用;不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化,这更像是集成测试;不能使用属性注入的方式构建不可变对象(final 修饰的变量)。
其次,Spring 开发团队建议 “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”,即在 beans 中永远使用基于构造器的依赖注入,对于必须的依赖,永远使用断言来确认。构造器注入有以下几个好处:
- 易于测试:构造器注入使得对类的单元测试变得更加容易。通过将依赖项作为构造函数的参数传递,可以轻松地在测试中传递模拟对象或存根对象,从而控制和验证类的行为。例如,对于一个OrderService类,其依赖于OrderRepository,在单元测试中,可以轻松传入模拟的OrderRepository对象,以验证OrderService的行为。
- 易于理解和维护:构造器注入提供了清晰的依赖关系,使代码更易于理解和维护。构造函数参数直观地表示了类所需的依赖项,降低了代码的复杂性。比如PaymentProcessor依赖于PaymentGateway,通过构造器注入一目了然。
- 依赖注入的一致性:构造器注入鼓励将所有依赖项都放在构造函数中,从而确保类的实例在被创建时处于一致的状态。这有助于避免在使用对象时遇到空指针异常或未初始化的依赖项。
- 不可变性:通过使用final关键字,构造器注入可以实现不可变性,这意味着一旦依赖项被设置,它们不能再被修改。这可以提高代码的安全性和稳定性。例如ShoppingCart类通过构造器注入一个不可变的List<Item>。
- 依赖项解析:构造器注入使依赖项的解析变得更加明确。当容器创建 Bean 实例时,容器只需查找所需的构造函数参数,而不需要进行复杂的解析或猜测。
- 避免循环依赖:构造器注入有助于避免循环依赖问题,因为在创建 Bean 实例时,构造函数参数必须已经可用。这有助于减少潜在的运行时错误。
综上所述,Spring 官方推荐构造器注入是出于提高代码质量、可测试性和可维护性的考虑。
相关文章:
《Spring 依赖注入方式全解析》
一、Spring 依赖注入概述 Spring 依赖注入(Dependency Injection,DI)是一种重要的设计模式,它在 Spring 框架中扮演着关键角色。依赖注入的核心概念是将对象所需的依赖关系由外部容器(通常是 Spring 容器)进…...
【C++动态规划】1411. 给 N x 3 网格图涂色的方案数|1844
本文涉及知识点 C动态规划 LeetCode1411. 给 N x 3 网格图涂色的方案数 提示 你有一个 n x 3 的网格图 grid ,你需要用 红,黄,绿 三种颜色之一给每一个格子上色,且确保相邻格子颜色不同(也就是有相同水平边或者垂直…...
外包干了3年,技术退步明显...
先说情况,大专毕业,18年通过校招进入湖南某软件公司,干了接近6年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能…...
SpringBoot 2.x 整合 Redis
整合 1)添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 如果没有使用下面给出的工具类,那么就不需要引入 -…...
React的API✅
createContext createContext要和useContext配合使用,可以理解为 “React自带的redux或mobx” ,事实上redux就是用context来实现的。但是一番操作下来我还是感觉,简单的context对视图的更新的细粒度把控比不上mobx,除非配合memo等…...
什么是全渠道客服中心?都包括哪些电商平台?
什么是全渠道客服中心?都包括哪些电商平台? 作者:开源呼叫中心系统 FreeIPCC,Github地址:https://github.com/lihaiya/freeipcc 全渠道客服中心是一种能够同时接入并处理来自多个渠道客户咨询和请求的综合服务平台。以…...
Jtti:如何知晓服务器的压力上限?具体的步骤和方法
了解服务器的压力上限(也称为性能极限或容量)是确保系统在高负载下仍能稳定运行的重要步骤。这通常通过压力测试(也称为负载测试或性能测试)来实现。以下是详细的步骤和方法来确定服务器的压力上限: 1. 定义测试目标和指标 在进行压力测试前,明确测试目标…...
贪心算法(1)
目录 柠檬水找零 题解: 代码: 将数组和减半的最少操作次数(大根堆) 题解: 代码: 最大数(注意 sort 中 cmp 的写法) 题解: 代码: 摆动序列࿰…...
SpringBoot,IOC,DI,分层解耦,统一响应
目录 详细参考day05 web请求 1、BS架构流程 2、RequestParam注解 完成参数名和形参的映射 3、controller接收json对象,使用RequestBody注解 4、PathVariable注解传递路径参数 5、ResponseBody(return 响应数据) RestController源码 6、统一响…...
目标驱动学习python动力
文章目录 迟迟未开始的原因打破思维里的围墙抛砖引玉爬虫 结束词 迟迟未开始的原因 其实我也是很早就知道有python,当时听说这个用于做测试不错,也就一直没有提起兴趣,后来人工智能火了之后,再次接触python,安装好pyth…...
力扣-Hot100-回溯【算法学习day.39】
前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向(例如想要掌握基础用法,该刷哪些题?)我的解析也不会做的非常详细,只会提供思路和一些关键点,力扣上的大佬们的题解质量是非常非常高滴&am…...
小熊派Nano接入华为云
一、华为云IoTDA创建产品 创建如下服务,并添加对应的属性和命令。 二、小熊派接入 根据小熊派官方示例代码D6完成了小熊派接入华为云并实现属性上传命令下发。源码:小熊派开源社区/BearPi-HM_Nano 1. MQTT连接代码分析 这部分代码在oc_mqtt.c和oc_mq…...
【linux硬件操作系统】计算机硬件常见硬件故障处理
这里写目录标题 一、故障排错的基本原则二、硬件维护注意事项三、关于最小化和还原出厂配置四、常见故障处理及调试五、硬盘相关故障六、硬盘相关故障:硬盘检测问题七、硬盘相关故障:自检硬盘报错八、硬盘相关故障:硬盘亮红灯九、硬盘相关故障…...
谈学生公寓安全用电系统的涉及方案
学生公寓安全 学生公寓安全用电系统的设计方案主要包括以下几个方面: 电气线路设计: 合理布线:确保所有电气线路按照国家或地区的电气安全标准进行设计,避免线路过载和短路。使用阻燃材料:选用阻燃或低…...
自动语音识别(ASR)与文本转语音(TTS)技术的应用与发展
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
Go 语言数组
Go 语言数组 引言 Go 语言是一种静态类型、编译型语言,由 Google 开发,旨在提高多核处理器下的编程效率。数组作为 Go 语言中的一种基本数据结构,提供了存储一系列具有相同类型元素的能力。本文将深入探讨 Go 语言中数组的使用方法、特性以…...
13. 【.NET 8 实战--孢子记账--从单体到微服务】--简易权限--完善TODO标记的代码
这篇文章特别短,短到可以作为一篇文章的一个章节,那让我们开始吧 一、编写代码 我们在代码中标记了大量的TODO标记,并且注明了这里暂时写死,等权限和授权完成后再改为动态获取这句话。那么到目前为止和权限有关的代码已经完成了…...
深入剖析Java内存管理:机制、优化与最佳实践
🚀 作者 :“码上有前” 🚀 文章简介 :Java 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 深入剖析Java内存管理:机制、优化与最佳实践 一、Java内存模型概述 1. Java内存模型的定义与作…...
【Amazon】亚马逊云科技Amazon DynamoDB 实践Amazon DynamoDB
Amazon DynamoDB 是一种完全托管的 NoSQL 数据库服务,专为高性能和可扩展性设计,特别适合需要快速响应和高吞吐量的应用场景,如移动应用、游戏、物联网和实时分析等。 工作原理 Amazon DynamoDB 在任何规模下响应时间一律达毫秒级ÿ…...
Qt-常用的显示类控件
QLabel QLabel有如下核心属性: 关于文本格式的验证: 其中<b>xxx<b>,就是加粗的意思。 效果: 或者再把它改为markdown形式的: 在markd中,#就是表示一级标题,我们在加上##后&#x…...
LabVIEW内燃机缸压采集与分析
基于LabVIEW开发的内燃机缸压采集与分析系统结合高性能压力传感器和NI数据采集设备,实现了内燃机工作过程中缸压的实时监测与分析,支持性能优化与设计改进。文中详细介绍了系统的开发背景、硬件组成、软件设计及其工作原理,展现了完整的开发流…...
【Linux学习】【Ubuntu入门】1-7 ubuntu下磁盘管理
1.准备一个U盘或者SD卡(插上读卡器),将U盘插入主机电脑,右键点击属性,查看U盘的文件系统确保是FAT32格式 2.右键单击ubuntu右下角图标,将U盘与虚拟机连接 参考链接 3. Ubuntu磁盘文件:/dev/s…...
VScode clangd插件安装
前提 在VScode中写C代码时,总会用到 C/C 这个插件,也就自然而然地使用了这个插件带来的代码跳转和代码提示功能。但是当代码变地很多时,就会变得非常慢。所以经过调查后弃用C/C 插件的这个功能,使用 clangd 这个插件来提示C代码和…...
【机器学习】- L1L2 正则化操作
目录 0.引言1.正则化的基本思想2.L1 正则化3.L2 正则化4.L1 与 L2 正则化的比较5.应用:控制模型复杂度6.超参数 λ \lambda λ 的选择7.总结 0.引言 在机器学习中,正则化是一种通过约束模型参数来控制模型复杂度的技术。它可以有效减少过拟合ÿ…...
Logback实战指南:基础知识、实战应用及最佳实践全攻略
背景 在Java系统实现过程中,我们不可避免地会借助大量开源功能组件。然而,这些组件往往功能丰富且体系庞大,官方文档常常详尽至数百页。而在实际项目中,我们可能仅需使用其中的一小部分功能,这就造成了一个挑战&#…...
基于python的机器学习(三)—— 关联规则与推荐算法
目录 一、关联规则挖掘 1.1 基本概念 1.2 Apriori算法 1.2.1 Apriori算法的原理 1.2.2 Apriori算法的实例 1.2.3 Apriori算法的程序实现(efficient-apriori模块) 1.3 FP-Growth算法 1.3.1 FP-Growth算法的原理 1.3.2 FP-Growth算法的实例 二、…...
【大模型】LLaMA: Open and Efficient Foundation Language Models
链接:https://arxiv.org/pdf/2302.13971 论文:LLaMA: Open and Efficient Foundation Language Models Introduction 规模和效果 7B to 65B,LLaMA-13B 超过 GPT-3 (175B)Motivation 如何最好地缩放特定训练计算预算的数据集和模型大小&…...
模拟器多开限制ip,如何设置单窗口单ip,每个窗口ip不同
很多手游多开玩家都是利用安卓模拟器实现手游多开,但是很多手游会限制ip,导致多开之后封号等问题,模拟器本身没有更换IP的功能,就需要通过第三方软件来实现 安卓模拟器概述 雷电模拟器、夜神模拟器、mum模拟器等都是目前市场上比较…...
hive的存储格式
1) 四种存储格式 hive的存储格式分为两大类:一类纯文本文件,一类是二进制文件存储。 Hive支持的存储数据的格式主要有:TEXTFILE、SEQUENCEFILE、ORC、PARQUET 第一类:纯文本文件存储 textfile: 纯文本文件存储格式…...
鸿蒙学习高效开发与测试-应用程序框架(3)
文章目录 1、应用程序框架1、规范化后台进程管理2、原生支持分布式3、支持多设备的统一窗口管理4、 组件共享及面向对象5、逻辑与界面解耦6、灵活扩展机制2、HarmonyOS SDK1、 开放能力 Kit2、开放能力的检索和使用3、 方舟工具链4、前端编译器架构1、应用程序框架 应 用 程 序…...
网站开发基于百度地图/教育培训机构网站
作为多用户操作系统,每一个登录的用户都会默认在 %USERPROFILE%\Local Settings\ 创建一个与登录名同名的目录,用来保存我的文档、桌面、收藏夹、应用程序设置等数据。但用户个人配置文件默认保存在系统分区。因为各种原因要重新安装操作系统,…...
新开家政如何做网站/百度公司名称
最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...
怎样做网站推广啊/关键词优化哪家强
概念 算法:任何一个良定义的计算过程,该过程取某个值或者值的集合作为输入并产生某个值或值的集合作为输出。这是比较概念化的定义,我们来分析一下,算法到底是什么,比如有一个问题:计算1到10所有整数的和&…...
php做电商网站/网站友情链接有什么用
paste -d | 第一个文件 第二个文件| 分隔符...
手机网站建设技术方案/建网站找哪个平台好呢
1.作业题目: 原生python实现knn分类算法,用鸢尾花数据集 2.算法分析: 最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来,当测试对象的属性和某个训练对象的属性完全匹配时,便可以对其进行分类。但是怎么…...
网站内容专题怎么做/百度上传自己个人简介
在这一小节,我会试着给出Java IO(java.io)包下所有类的概述。更具体地说,我会根据类的用途对类进行分组。这个分组将会使你在未来的工作中,进行类的用途判定时,或者是为某个特定用途选择类时变得更加容易。 输入和输出 – 数据源和…...