Spring IOC(控制反转)和DI(依赖注入)在Spring框架中的核心作用

1.1 IoC(控制反转)和DI(依赖注入)在Spring框架中的核心作用

  1. IoC(控制反转):
  • IoC是一种编程思想,其目标是将对象的创建和依赖关系的维护从应用程序代码中分离出来,交给一个外部的容器(如Spring IoC容器)来管理。
  • 在IoC中,控制权从应用程序代码(即调用者)转移到了外部容器。容器负责创建对象、管理对象的生命周期以及处理对象之间的依赖关系。
  • 通过IoC,应用程序代码变得更加简洁和模块化,因为它不再需要关心对象的创建和依赖关系的维护。
  1. DI(依赖注入):
  • DI是IoC的一种具体实现方式,也是Spring框架的核心特性之一。
  • 在DI中,对象之间的依赖关系不是通过硬编码的方式(如使用new关键字)来建立的,而是由容器在运行时动态地将依赖关系注入到对象中。
  • DI有三种主要的实现方式:构造器注入、setter方法注入和字段注入。
  • 通过DI,我们可以更加灵活地管理对象之间的依赖关系,因为我们可以在运行时动态地改变依赖关系,而无需修改应用程序代码。
  1. IoC和DI的关系:
  • IoC和DI是密切相关的概念。IoC是一种编程思想,而DI是实现IoC的一种具体技术。
  • 当我们说“使用IoC”时,我们实际上是在说“使用DI来实现IoC”。
  • 在Spring框架中,IoC容器通过DI来管理对象之间的依赖关系,并实现了控制权的反转。
  1. IoC和DI的好处:
  • 降低了代码之间的耦合度:由于依赖关系是通过容器来管理的,因此应用程序代码不再需要关心对象的创建和依赖关系的维护,从而降低了代码之间的耦合度。
  • 提高了代码的可测试性:由于依赖关系可以通过配置文件或注解来定义,因此我们可以轻松地替换依赖对象来进行单元测试或集成测试。
  • 提高了代码的可维护性和可扩展性:由于应用程序代码不再需要关心对象的创建和依赖关系的维护,因此我们可以更加容易地修改或扩展代码。

Spring的依赖注入(Dependency Injection, 简称DI) 是Spring框架的核心功能之一,它是一种实现对象间解耦、降低代码耦合度的技术。依赖注入的核心思想是将对象所依赖的外部资源(如对象、值、配置等)不由其内部创建或查找,而是由外部容器(如Spring容器)在运行时动态地注入到对象中。
具体来说,依赖注入有以下几个关键点:

  1. 注入方式:Spring提供了多种依赖注入的方式,包括构造函数注入、setter方法注入和字段注入等。
  • 构造函数注入:通过调用类的构造函数并将所需依赖作为参数传递来实现。
  • setter方法注入:通过调用类的setter方法来设置依赖。
  • 字段注入:直接在字段上使用注解(如@Autowired)来注入依赖,但这种方式通常不推荐,因为它破坏了封装性。
  1. 注入时机:依赖注入通常发生在Spring容器创建或更新Bean实例时。
  2. 注入源:Spring容器是依赖注入的源头,它负责创建和管理Bean实例,并在适当的时候将依赖注入到Bean中。

DI有两个主要的变体:

  1. 构造函数注入:
  • 当使用构造函数注入时,Spring IoC容器会尝试解析Bean的依赖关系,并通过调用Bean的构造函数来创建其实例。如果在这个过程中遇到循环依赖,即Bean A依赖于Bean B,而Bean B又依赖于Bean A,那么当Spring试图注入其中一个Bean的依赖时,另一个Bean可能还没有完全创建好,因为Spring还在调用它的构造函数。
  • 由于构造函数的特性(它必须在对象创建时调用,并且只能调用一次),Spring IoC容器无法解决这种循环依赖,因为此时它不能注入一个还没有完全创建好的Bean实例。因此,Spring会抛出一个BeanCurrentlyInCreationException异常。
  1. Setter注入:
  • Setter注入允许在Bean的构造函数调用之后,通过调用其setter方法来注入依赖项。这种注入方式使得Spring IoC容器能够处理循环依赖。
  • 当Spring遇到循环依赖时,它会首先为每个Bean创建一个实例(调用其无参构造函数或具有默认值的构造函数),然后逐个设置这些Bean的依赖项(通过调用setter方法)。由于这些Bean实例在调用setter方法之前已经创建好了,因此Spring可以安全地解决循环依赖问题。

基于构造器的依赖注入(Constructor-based Dependency Injection)是一种在对象创建时通过其构造函数来传递依赖项的技术。这种依赖注入方式有几个优点,比如确保依赖项在对象被完全初始化之前就已经被设置,以及确保依赖项在对象的整个生命周期内都是不可变的(除非对象被重新创建)。

在Spring框架中,你可以使用基于构造器的依赖注入来配置你的beans。以下是一个简单的例子,展示了如何在Spring的XML配置文件中使用基于构造器的依赖注入:

<bean id="myService" class="com.example.MyService">  <constructor-arg ref="myDependency" />  
</bean>  <bean id="myDependency" class="com.example.MyDependency" />

在这个例子中,MyService类有一个构造函数,它接受一个MyDependency类型的参数。在Spring的配置文件中,我们定义了一个id为myService的bean,它的类为com.example.MyService。然后,我们使用<constructor-arg>元素来指定构造函数参数的值,这里我们使用ref属性来引用另一个id为myDependency的bean。

在Java类中,对应的构造函数可能如下所示:

package com.example;  public class MyService {  private final MyDependency myDependency;  public MyService(MyDependency myDependency) {  this.myDependency = myDependency;  }  // ... 其他方法和属性 ...  
}  
class MyDependency {  // ... 类的定义 ...  
}

在这个例子中,MyService类有一个私有的final字段myDependency,它只能在构造函数中被初始化。这确保了myDependency在MyService对象的整个生命周期内都是不可变的。

如果你更喜欢使用Java配置(即使用@Configuration和@Bean注解的方式),你也可以这样做:

@Configuration  
public class AppConfig {  @Bean  public MyDependency myDependency() {  return new MyDependency();  }  @Bean  public MyService myService(MyDependency myDependency) {  return new MyService(myDependency);  }  
}

在这个Java配置类中,我们定义了两个@Bean方法,分别用于创建MyDependency和MyService的实例。myService方法接受一个MyDependency类型的参数,这个参数是通过调用myDependency()方法获取的。这样,Spring就会在创建MyService实例时自动调用myDependency()方法,并将返回的MyDependency实例传递给MyService的构造函数。

基于Setter的依赖注入是一种在对象创建后,通过调用其setter方法来设置依赖项的技术。这种方法允许对象在其生命周期的某个时刻获得其依赖项,而不是在构造时就必须知道所有的依赖项。以下是使用基于Setter的依赖注入的示例:

  1. 定义依赖类

首先,我们定义一个依赖类UserDao,它可能是一个数据访问对象(DAO)类,用于与数据库交互。

public class UserDao {  // 方法和属性...  
}
  1. 定义需要注入依赖的类

接下来,我们定义一个需要UserDao实例的类UserService。该类包含一个私有的UserDao字段和一个公开的setter方法,用于设置该字段。

public class UserService {  private UserDao userDao;  // Setter方法用于注入UserDao实例  public void setUserDao(UserDao userDao) {  this.userDao = userDao;  }  // 其他方法,如使用userDao进行操作...  
}
  1. 在Spring配置文件中配置依赖注入

在Spring的XML配置文件中,我们定义了两个bean:一个用于UserDao,另一个用于UserService。对于UserService bean,我们使用<property>元素和ref属性来指定userDao属性的值,该值是对userDao bean的引用。

<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans.xsd">  <bean id="userDao" class="com.example.UserDao"/>  <bean id="userService" class="com.example.UserService">  <property name="userDao" ref="userDao"/>  </bean>  </beans>

当Spring容器启动时,它会读取配置文件并创建定义的bean。对于userService bean,Spring会首先创建一个UserService的实例,然后调用其setUserDao方法,并将userDao bean的实例作为参数传递。这样,UserService就获得了它所需的UserDao依赖项。

字段注入是一种在Spring框架中常见的依赖注入方式,其中使用注解如@Autowired、@Inject或@Resource直接在类的字段上声明依赖。Spring容器会在创建bean的实例时自动查找匹配的bean来注入。尽管字段注入在某些情况下可能看起来更简洁,但它确实可能破坏封装性,因为依赖项是通过反射直接设置的,而不是通过类的构造函数或setter方法。
下面是一个使用字段注入的示例:

import org.springframework.beans.factory.annotation.Autowired;  public class UserService {  // 直接在字段上使用@Autowired注解进行注入  @Autowired  private UserDao userDao;  // 由于已经通过字段注入了依赖,这里不需要提供setter方法  // 使用注入的userDao进行操作的方法  public void performAction() {  // 使用userDao进行操作  userDao.doSomething();  }  
}  // 假设的UserDao类  
public class UserDao {  public void doSomething() {  // 模拟数据库操作或其他业务逻辑  }  
}  // Spring配置(如果使用Java配置)  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  @Configuration  
public class AppConfig {  @Bean  public UserDao userDao() {  return new UserDao();  }  @Bean  public UserService userService() {  return new UserService(); }  
}

在上面的示例中,UserService类有一个UserDao类型的字段userDao,该字段上使用了@Autowired注解。当Spring容器创建UserService的bean时,它会查找一个类型为UserDao的bean来自动注入到userDao字段中。同样,AppConfig类配置了UserDao和UserService的bean定义。

尽管字段注入在某些情况下很方便,但它通常不是推荐的做法,因为它违反了封装原则,允许外部代码直接修改类的内部状态。此外,字段注入可能会使测试更加困难,因为依赖项是直接在字段上注入的,而不是通过构造函数或setter方法,这使得在测试中提供模拟依赖项变得更加复杂。因此,在很多情况下,构造函数注入或setter注入是更好的选择。

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

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

相关文章

Postman接口测试之postman设置接口关联,实现参数化

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman设置接口关联 在实际的接口测试中&#xff0c;后一个接口经常需要用到前一个接口返回的结…

beego项目发布到linux

beego项目发布到linux 1.打包应用程序2.上传压缩包&#xff08;beego-blog.tar.gz&#xff09;到服务器指定目录3.解压缩包4.先进入项目目录5.添加执行权限6.后台运行beego程序7.nginx配置域名及反向代理8.浏览器访问域名查看网站 1.打包应用程序 bee pack -be GOOSlinux2.上传…

PAT B1012. 数字分类

题目描述 给定一系列正整数,请按要求对数字进行分类,并输出以下五类数字: Al-能被5整除的数字中所有偶数的和; A2将被5除后余1的数字按给出顺序进行交错求和,即计算n1-n2n3-n4…; A3被5除后余2的数字的个数; A4被5除后余3的数字的平均数,精确到小数点后一位; A5被5除后余4的…

苹果电脑下载vite包错

苹果电脑下载vite包错/Users/lili/.npm/_cacache/index-v5/c5/50/b451703d03b3802b9ee6b7ff2b0bde4de7f26830eb52c904d6911c137cf8包错解决方式 解决方式&#xff1a;sudo chown -R 501:20 "/Users/wangxin/.npm"

PCI-E 5.0固态硬盘温度价格「双降」,速度近15GB/s

都 2024 年了&#xff0c;相信各位同学对固态硬盘都不陌生了吧。 随着技术的不断更新迭代&#xff0c;固态硬盘接口速率如今最高已经来到了 PCI-e 5.0 了。 其实这不算什么新技术了&#xff0c;早在2023年5月美光就上市了全球首款 PCI-e 5.0 固态硬盘&#xff0c; 英睿达 T700…

Python学习打卡:day09

day9 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day964、字典课后习题65、5类数据容器的总结对比数据容器分类数据容器特点对比 66、数据容器的通用操作遍历统计len、max 和 min 转换list(容器)、…

算法基础精选题单 枚举 (合适的枚举顺序+合适的枚举内容+前缀和和差分) (个人题解)

前言&#xff1a; 今日第一份题解&#xff0c;题目主要是于枚举有关&#xff0c;枚举算是算法题中较为简单的部分了&#xff08;对我来说还是有些难想的&#xff09;&#xff0c;话不多说&#xff0c;见下。 正文&#xff1a; 题单&#xff1a;237题】算法基础精选题单_ACM竞赛…

概念描述——TCP/IP模型中的两个重要分界线

TCP/IP模型中的两个重要分界线 协议的层次概念包含了两个也许不太明显的分界线&#xff0c;一个是协议地址分界线&#xff0c;区分出高层与低层寻址操作&#xff1b;另一个是操作系统分界线&#xff0c;它把系统与应用程序区分开来。 高层协议地址界限 当我们看到TCP/P软件的…

el-radio单选框的选中与取消

需求是有一个单选框&#xff0c;点击选中&#xff0c;再次点击取消。 第一次写的时候代码如下&#xff1a; <el-radio-group v-model"keyForm.highlight"><el-radio:value"true"click"handleClick">是</el-radio> </el-r…

Eigen中 Row-Major 和 Column-Major 存储顺序的区别

Eigen中 Row-Major 和 Column-Major 存储顺序的区别 flyfish Eigen::RowMajor 是 Eigen 库中用于指定矩阵存储顺序的一种选项 理解 Row-Major 和 Column-Major 存储顺序的区别&#xff0c;绘制一个单一的图来显示内存中的元素访问顺序,在图中用箭头表示访问顺序. import nu…

Python基础学习笔记(十一)——集合

目录 一、集合的介绍与创建二、集合的存储原理三、元素的修改1. 添加元素2. 删除元素 四、集合的运算五、集合的判定 一、集合的介绍与创建 集合&#xff08;set&#xff09;&#xff0c;一种可变、无序、不重复的数据结构&#xff0c;由大括号{}内、用逗号分隔的一组元素组成。…

Redis面试题自测

文章目录 一、Redis 有什么作用?为什么要用 Redis为什么要用缓存&#xff1f;二、Redis 除了做缓存&#xff0c;还能做什么&#xff1f;三、Redis 可以做消息队列么&#xff1f;四、分布式缓存常⻅的技术选型⽅案有哪些&#xff1f;五、Redis 常⽤的数据结构有哪些&#xff1f…

BarTender软件最新版下载-bartender条码标签打印软件下载

​​BarTender​​是一款遵循“look and feel”标准的​​条码打印​​软件。​​BarTender​​条码打印软件能够帮助用户挥洒自如&#xff0c;轻松制作出标签条码&#xff0c;包括文本、图形、​​条形码​​和大多数序列化功能。BarTender条码打印软件功能强大、操作简单&…

以太坊智能合约不能调用:一定注意智能合约地址,每次部署地址都会变化;nonce值 什么作用,是什么;在交易中调用智能合约添加附加信息

目录 以太坊智能合约不能调用 一定注意智能合约地址,每次部署地址都会变化 Transaction must include these fields: %r" % missing_keys 缺少nonce nonce值 什么作用,是什么 在交易中调用智能合约添加附加信息 1. 定义智能合约 2. 部署并调用智能合约 注意事项…

基于YOLOv10深度学习的高密度人脸智能检测与统计系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

C++之STL(一)

1、泛型程序设计 目的&#xff1a;提供相同的算法&#xff0c;相同的逻辑&#xff0c;来对不同类型的数据结构进行操作。 所以需要将类型当作参数&#xff0c;也就是参数类型化。 2、什么是STL STL是基于模板实现的。编译的时候进行实例化 3、STL组件 4、容器算法迭代器关系 …

Flink 计数器Accumulator

简述 在 Apache Flink 中&#xff0c;Accumulator 是一个用于收集作业执行期间聚合信息的工具。它允许在用户定义的函数&#xff08;如 MapFunction, FlatMapFunction, ProcessFunction 等&#xff09;中累积值&#xff0c;并在作业完成后检索这些值。这对于跟踪诸如事件数量、…

使用 Hugging Face 推理终端搭建强大的“语音识别 + 说话人分割 + 投机解码”工作流

Whisper 是当前最先进的开源语音识别模型之一&#xff0c;毫无疑问&#xff0c;也是应用最广泛的模型。如果你想部署 Whisper 模型&#xff0c;Hugging Face推理终端能够让你开箱即用地轻松部署任何 Whisper 模型。但是&#xff0c;如果你还想叠加其它功能&#xff0c;如用于分…

项目实战中学透Spring-业务场景驱动-Spring01(IOCDI)

软件环境 JDK1.8 Maven3.6 IDEA2022.3(Ultimate Edition) Spring5.3.29 主要知识点大纲 1.Spring简介 2.Spring整体架构 3.业务场景中理解Spring IOC(控制反转)和DI(依赖注入) 4.业务场景中理解IOC容器&#xff0c;实例化容器&#xff0c;实例化Bean的几种方式 5.业务…

java基础·小白入门(一)

目录 Java语言概述Java的性质三种平台跨平台原理 Java语言开发环境相关概念Java开发工具的安装Java程序的编译与运行基本注意事项 Java语言基础数据类型基本数据类型引用数据类型 关键字与标识符常量与变量常量变量 数据类型转换常见运算符 Java语言概述 这一部分主要讲讲Java的…