当前位置: 首页 > news >正文

Spring之依赖注入(DI)和控制反转(IoC)——配置文件、纯注解

依赖注入

依赖注入(Dependency Injection,简称 DI)与控制反转(loC)的含义相同,只不过这两
个称呼是从两个角度描述的同一个概念。对于一个 Spring 初学者来说,这两种称呼很难理解,
下面我们将通过简单的语言来描述这两个概念。

当Java对象(调用者)需要调用另一个Java对象(被调用者 即被依赖对象)时,传统模式下 调用者会采用“new 被调用者”的方式来创建对象 这种方式会导致调用者和被调用者之间的耦合度增加

创建两个用户User1和User2 使User2依赖于User1

public class User1 {public void say(){System.out.println("User1说");}
}
public class User2 {private User1 user1;public void setUser1(User1 user1) {this.user1 = user1;}public void say(){user1.say();System.out.println("User2说");}
}

如果我们想使用User2的say()方法 需要先实例化User1对象 否则无法使用

正确应为

控制反转 

在使用Spring框架后 对象的实例不再由调用者(User2)来进行创建 而是由Spring容器实现 Spring 容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了 Spring 容器,控制权发生了反转,这就是 Spring 的控制反转

从Spring的角度来看 Spring的容器负责将被依赖对象(User1)赋值给调用者(User2)的成员变量 这相当于为调用者注入了它的依赖实例 这就是Spring的依赖注入

基于xml配置文件的方式实现Bean管理和注入属性

属性setter方法注入

指 loC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参静态工厂方法实例化 Bean后,调用该Bean 的 setter 方法,即可实现基于 setter 方法的依赖注入。

创建UserDao1和UserDao2

public interface UserDao1 {void say();
}
public interface UserDao2 {void say();
}

创建两个接口的实现类 并让User2的实现类依赖于User1

public class UserDao1Impl implements UserDao1 {@Overridepublic void say() {System.out.println("UserDao1说");}
}
public class UserDao2Impl implements UserDao2 {private UserDao1 userDao1;public void setUserDao1(UserDao1 userDao1) {this.userDao1 = userDao1;}public void say(){userDao1.say();System.out.println("UserDao2说");}
}

在applicationContext.xml中写入

<bean id="userDao1" class="com.qcby.spring.DaoImpl.UserDao1Impl"></bean><bean id="userDao2" class="com.qcby.spring.DaoImpl.UserDao2Impl"><property name="userDao1" ref="userDao1"></property>
</bean>

其中

 

测试类中

@Testpublic void UserDaoTest(){/*从类路径classpath 中寻找到xml文件 完成applicationContext实例*/ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");/*通过getBean获取配置文件中的信息 完成实例化*/UserDao2 userDao2 = (UserDao2) applicationContext.getBean("userDao2");userDao2.say();}

结果为

我们可以看到没有“new UserDao1”也实现了上述操作  实现了依赖注入

属性的set方法注入值

创建一个新的User对象

编写属性,提供该属性对应的set方法,编写配置文件完成属性值的注入

public class User {// 编写成员属性,一定需要提供该属性的set方法//IOC容器底层就通过属性的set方法方式注入值private int age;private String name;private Demo demo;public void setAge(int age) {this.age = age;}public void setName(String name) {this.name = name;}public void setDemo(Demo demo) {this.demo = demo;}@Overridepublic String toString() {return "User{" +"age=" + age +", name='" + name + '\'' +", demo=" + demo +'}';}
}
<!‐‐DI:依赖注入‐‐>
<bean id="user" class="com.qcby.service.User" ><!--使用property完成属性注入name:类里面属性名称value:向属性注入值ref:对象映射--><property name="age" value="18"></property><property name="name" value="张三"></property><property name="demo" ref="demo"></property>
</bean>

数组,集合(List,Set,Map)等的set注入

public class CollectionBean {private String [] strs;private List<String> list;private Map<String,String> map;public void setStrs(String[] strs) {this.strs = strs;}public void setList(List<String> list) {this.list = list;}public void setMap(Map<String, String> map) {this.map = map;}@Overridepublic String toString() {return "CollectionBean{" +"strs=" + Arrays.toString(strs) +", list=" + list +", map=" + map +'}';}
}
<!‐‐给集合属性注入值‐‐>
<bean id="collectionBean" class="com.qcby.service.CollectionBean"><property name="strs"><array><value>美美</value><value>小凤</value></array></property><property name="list"><list><value>熊大</value><value>熊二</value></list></property><property name="map"><map><entry key="aaa" value="老王"/><entry key="bbb" value="小王"/></map></property>
</bean>

通过构造方法注入

我们将UserDao2Impl中的setter方法进行删除  并添加上UserDao1的构造方法(构造方法是必不可少的)

public class UserDao2Impl implements UserDao2 {private UserDao1 userDao1;public UserDao2Impl(UserDao1 userDao1) {this.userDao1 = userDao1;}public void say(){userDao1.say();System.out.println("UserDao2说");}
}

在applicationContext.xml中写入 通过constructor-arg进行注入

<bean id="userDao1" class="com.qcby.spring.DaoImpl.UserDao1Impl"></bean><bean id="userDao2" class="com.qcby.spring.DaoImpl.UserDao2Impl"><constructor-arg ref="userDao1"></constructor-arg></bean>

测试类中结果为

在注入的同时进行赋值操作

对于类成员变量,构造函数注入

public class Car {// 名称private String cname;// 金额private Double money;public Car(String cname,Double money){this.cname = cname;this.money = money;}@Overridepublic String toString() {return "Car{" +"cname='" + cname + '\'' +", money=" + money +'}';}
}
<bean id="car" class="com.qcby.service.Car"><constructor-arg name="cname" value="奔驰"></constructor-arg><constructor-arg name="money" value="35"></constructor-arg>
</bean>

数组,集合(List,Set,Map)等的构造器注入

private String[] Strings;
private List<String> list;
private Map<String,String> map;public UserService( String[] Strings, List<String> list, Map<String, String> map) {this.Strings = Strings;this.list = list;this.map = map;
}
<bean id="user" class="com.qcby.service.UserService"><constructor-arg index="0"><array><value>aaa</value><value>bbb</value><value>ccc</value></array></constructor-arg><constructor-arg index="1"><list><value>小黑</value><value>小白</value></list></constructor-arg><constructor-arg index="2"><map><entry key="aaa" value="小黑"/><entry key="bbb" value="小号"/></map></constructor-arg>
</bean>

基于注解的方式实现Bean管理和注入属性

Spring针对Bean管理中创建对象提供的注解

  1. @Component 普通的类
  2. @Controller 表现层
  3. @Service 业务层
  4. @Repository 持久层

上边四个功能一样,都可以用来创建bean实例

在进行注解开发之前要现在配置文件中进行相关配置

编写对应的接口和实现类

public interface UserDao1 {void say();
}
@Controller(value="UserDao1")
public class UserDao1Impl implements UserDao1 {@Overridepublic void say() {System.out.println("UserDao1说");}
}

其中

用注解的方实现属性注入

  1. @Value 用于注入普通类型(String,int,double等类型)
  2. @Autowired 默认按类型进行自动装配(引用类型)
  3. @Qualifier 不能单独使用必须和@Autowired一起使用,强制使用名称注入
  4. @Resource Java提供的注解,也被支持。使用name属性,按名称注入

 创建一个实体类Car

@Component(value = "c")
// @Controller
// @Service(value = "c")
// @Repository(valu = "c")
public class Car {// 注解注入值,属性set方法是可以省略不写的。// 只有一个属性,属性的名称是value,value是可以省略不写的@Value("大奔2")private String cname;@Value(value = "400000")private Double money;// 也不用提供set方法// 按类型自动装配的注解,和id名称没有关系@Autowired// 按id的名称注入,Qualifier不能单独使用,需要Autowired一起使用。// @Qualifier(value = "person")// @Resource Java提供的注解,按名称注入对象,属性名称是name// @Resource(name = "person")private Person person;@Overridepublic String toString() {return "Car{" +"cname='" + cname + '\'' +", money=" + money +", person=" + person +'}';}}

再在Car中的Person引用类进行注解注入

@Controller
//@Component(value = "person") 
//此处没有对Person的使用 故可以不设置value值
public class Person {@Value("张三")private String pname;@Overridepublic String toString() {return "Person{" +"pname='" + pname + '\'' +'}';}}

在测试类中进行测试输出

@Testpublic void CarTest(){/*从类路径classpath 中寻找到xml文件 完成applicationContext实例*/ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");/*通过getBean获取配置文件中的信息 完成实例化*/Car car = (Car) applicationContext.getBean("car");System.out.println(car);}

 IOC纯注解的方式代替配置文件

纯注解的方式是微服务架构开发的主要方式,所以也是非常的重要。纯注解的目的是替换掉所有的配置文件。但是需要编写配置类。

常用的注解总结

  1. @Configuration 声明是配置类
  2. @ComponentScan 扫描具体包结构的

编写实体类

@Component
public class Order {@Value("北京")private String address;@Overridepublic String toString() {return "Order{" +"address='" + address + '\'' +'}';}
}

编写配置类,替换掉applicationContext.xml配置文件

@Configuration
// 扫描指定的包结构
@ComponentScan(value = "com.qcby")
public class SpringConfig {
}

测试方法的编写

package com.qcby.test;
import com.qcby.demo4.Order;
import com.qcby.demo4.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Demo4 {@Testpublic void run(){// 创建工厂,加载配置类 // 此处new的对象和配置文件中不同ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);// 获取到对象Order order = (Order) ac.getBean("order");System.out.println(order);}
}

相关文章:

Spring之依赖注入(DI)和控制反转(IoC)——配置文件、纯注解

依赖注入 依赖注入(Dependency Injection&#xff0c;简称 DI)与控制反转(loC)的含义相同&#xff0c;只不过这两 个称呼是从两个角度描述的同一个概念。对于一个 Spring 初学者来说&#xff0c;这两种称呼很难理解, 下面我们将通过简单的语言来描述这两个概念。 当Java对象&…...

基于SpringBoot的宠物健康咨询系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;宠物健康知识信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不…...

Lucene的使用方法与Luke工具(2)

文章目录 第2章 Lucene快速入门2.1 项目搭建2.1.1 SQL语句2.1.2 maven依赖2.1.3 实体类&#xff1a;2.1.4 编写DAO&#xff1a; 2.2 建立索引2.2.1 步骤&#xff1a;2.2.2 实现代码&#xff1a; 2.3 Luke工具2.3.1 运行界面介绍&#xff1a;1&#xff09;主界面2&#xff09;文…...

【客户端开发】electron 中无法使用 js-cookie 的问题

产生问题的原因 谷歌浏览器升级之后&#xff0c;出于安全考虑&#xff0c;cookie的SameSite属性默认值由None变为Lax&#xff0c;对于跨域的请求&#xff0c;禁止携带cookie。electron内核是chromium内核,所以也会有这个限制。 Cookie的SameSite属性用来限制第三方 Cookie&…...

kafka客户端消费者吞吐量优化

问题背景 业务场景 mq消息消费实时性要求不高&#xff0c;期望可以牺牲一部分实时性&#xff0c;换取吞吐量&#xff0c;例如&#xff1a;数据库单条insert优化为batchInsert。优化后结果不符合预期&#xff1a;消费者消费消息的batchSize远小于实际配置的max.poll.records&a…...

电子工程师-高质量工具包

目录 来源 高质量工具包介绍 总体框架如下 ZL01-各类元器件相关资料 ZL02-电源设计资料 ZL03-大厂参考资料 ZL04-开发工具 ZL05-仿真工具 ZL06-各类电路接口设计指南 ZL07-付费专栏全集 ZL08-优质电子书 ZL09-硬件工程师 ZL10FPGA工程师教程 ZL10-PCB设计教程 Z…...

简单认识redis - 12 redis锁

在斜体样式**redis中&#xff0c;不同的问题有不一样的解决办法&#xff0c;那么锁也有不同的锁来解决不一样的问题&#xff0c;下面将举出几个常用的redis锁。 1. SETNX锁&#xff08;简单独占锁&#xff09; 原理&#xff1a; SETNX&#xff08;SET if Not eXists&#xff…...

基于springboot+vue车辆充电桩管理系统

基于springbootvue车辆充电桩管理系统 摘 要 随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;车辆充电桩管理系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;…...

shodan用法(完)

声明 学习视频来自B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 shodan 今天&#xff0c;我们把shoda…...

【若依框架】代码生成详细教程,15分钟搭建Springboot+Vue3前后端分离项目,基于Mysql8数据库和Redis5,管理后台前端基于Vue3和Element Plus,开发小程序数据后台

今天我们来借助若依来快速的搭建一个基于springboot的Java管理后台&#xff0c;后台网页使用vue3和 Element Plus来快速搭建。这里我们可以借助若依自动生成Java和vue3代码&#xff0c;这就是若依的强大之处&#xff0c;即便你不会Java和vue开发&#xff0c;只要跟着石头哥也可…...

转子侧串级调速系统和双馈调速系统

转子侧串级调速系统和双馈调速系统是两种不同的电机调速技术&#xff0c;它们在基本原理、效率以及应用场景等方面存在区别。以下是详细的对比分析&#xff1a; 基本原理 转子侧串级调速系统&#xff1a;通过在绕线式异步电动机的转子回路中串入一个可调节的附加电势&#xff0…...

AI学习指南自然语言处理篇-Transformer模型的实践

AI学习指南自然语言处理篇 - Transformer模型的实践 目录 引言Transformer模型概述 自注意力机制编码器-解码器结构 环境准备Transformer模型的实现 编码器实现解码器实现Transformer模型整体实现 Transformer在NLP任务中的应用 文本分类机器翻译 总结与展望 引言 在过去的数…...

【LVGL速成】LVGL修改标签文本(GUI Guider生成的字库问题)

目录 前置篇章&#xff1a; 一.问题背景 二.失败方案 三.成功方案 1.Gui guider的源码结构 2.手动生成字体 3.Keil中配置相关文件 ​编辑 4.修改文字 四.字体样式函数说明 前置篇章&#xff1a; 【LVGL快速入门(二)】LVGL开源框架入门教程之框架使用(UI界面设计)_lvgl…...

C语言项目实践-贪吃蛇

⽬录&#xff1a; 1. 游戏背景 2. 游戏效果演⽰ 3. 实现的⽬标 4. 实现的定位 5. 技术要点 6. 贪吃蛇游戏设计与分析 7. 贪吃蛇游戏数据结构设计 8. 相关Win32API介绍 9. 参考代码 正文开始 1. 游戏背景 贪吃蛇是久负盛名的游戏&#xff0c;它也和俄罗斯⽅块&#xf…...

在kanzi 3.9.8里使用API创建自定义材质

1. kanzi studio设置 1.1 创建一个纹理贴图&#xff0c;起名Render Target Texture 1.2 创建一个Image节点&#xff0c;使用该贴图 2. 代码设置 2.1 创建一个自定义节点类 class mynode2d : public Node2D { public: virtual void renderOverride(Renderer3D& renderer…...

IDEA中通义灵码的使用技巧

大家好&#xff0c;我是 V 哥。在日常写代码的过程中&#xff0c;通过 AI 工具辅助开发已是当下程序员惯用的方式&#xff0c;V 哥在使用了众多的 AI 工具后&#xff0c;多数情况下&#xff0c;选择通义灵码来辅助开发&#xff0c;尤其是解释代码和生成单元测试功能甚是好用&am…...

JS中let var 和const区别

在JavaScript中&#xff0c;let、var 和 const 都是用来声明变量的关键字&#xff0c;但它们之间有几个关键的区别&#xff1a; 作用域&#xff08;Scope&#xff09;: var 声明的变量拥有函数作用域&#xff08;function scope&#xff09;&#xff0c;这意味着如果 var 变量在…...

ansible详细介绍和具体步骤

Ansible简介 1.1 Ansible的基本概念 Ansible是一款开源的自动化工具&#xff0c;旨在简化IT操作的复杂性。它由Michael DeHaan创建&#xff0c;并于2012年发布&#xff0c;随后在2015年被Red Hat收购。Ansible的核心理念是“简单即美”&#xff0c;它通过使用YAML&#xff08…...

利用LangChain与LLM打造个性化私有文档搜索系统

我们知道LLM&#xff08;大语言模型&#xff09;的底模是基于已经过期的公开数据训练出来的&#xff0c;对于新的知识或者私有化的数据LLM一般无法作答&#xff0c;此时LLM会出现“幻觉”。针对“幻觉”问题&#xff0c;一般的解决方案是采用RAG做检索增强。 但是我们不可能把…...

linux中的软、硬链接

目录 引言 简单介绍 如何理解软硬链接 链接的应用 环路问题 引言 在Linux操作系统的广阔天地中&#xff0c;文件管理是其核心功能之一。而软链接和硬链接作为Linux文件系统中的两种特殊链接方式&#xff0c;它们为用户提供了灵活的文件访问途径和高效的磁盘空间利用手段。…...

求解插值多项式及其余项表达式

例 求满足 P ( x j ) f ( x j ) P(x_j) f(x_j) P(xj​)f(xj​) ( j 0 , 1 , 2 j0,1,2 j0,1,2) 及 P ′ ( x 1 ) f ′ ( x 1 ) P(x_1) f(x_1) P′(x1​)f′(x1​) 的插值多项式及其余项表达式。 解&#xff1a; 由给定条件&#xff0c;可确定次数不超过3的插值多项式。…...

什么是「镜像」?(Docker Image)

&#x1f9ca; 什么是「镜像」&#xff1f;&#xff08;Docker Image&#xff09; &#x1f4a1; 人话解释&#xff1a; Docker 镜像就像是一个装好程序的“快照包”&#xff0c;里面包含了程序本体、依赖库、运行环境&#xff0c;甚至是系统文件。 你可以把镜像理解为&…...

多模态大语言模型arxiv论文略读(109)

Math-PUMA: Progressive Upward Multimodal Alignment to Enhance Mathematical Reasoning ➡️ 论文标题&#xff1a;Math-PUMA: Progressive Upward Multimodal Alignment to Enhance Mathematical Reasoning ➡️ 论文作者&#xff1a;Wenwen Zhuang, Xin Huang, Xiantao Z…...

Impromptu VLA:用于驾驶视觉-语言-动作模型的开放权重和开放数据

25年5月来自清华和博世的论文“Impromptu VLA: Open Weights and Open Data for Driving Vision-Language-Action Models”。 用于自动驾驶的“视觉-语言-动作” (VLA) 模型前景光明&#xff0c;但在非结构化极端场景下却表现不佳&#xff0c;这主要是由于缺乏有针对性的基准测…...

TDengine 开发指南——无模式写入

简介 在物联网应用中&#xff0c;为了实现自动化管理、业务分析和设备监控等多种功能&#xff0c;通常需要采集大量的数据项。然而&#xff0c;由于应用逻辑的版本升级和设备自身的硬件调整等原因&#xff0c;数据采集项可能会频繁发生变化。为了应对这种挑战&#xff0c;TDen…...

配置git命令缩写

以下是 Git 命令缩写的配置方法及常用方案&#xff0c;适用于 Linux/macOS/Windows 系统&#xff1a; &#x1f527; 一、配置方法 1. 命令行设置&#xff08;推荐&#xff09; # 基础命令缩写 git config --global alias.st status git config --global alias.co che…...

SpringBoot2.3.1集成Knife4j接口文档

首先要查看项目中pom文件里面有没有swagger和knife4j的依赖&#xff0c;如果有的话删除&#xff0c;加入以下依赖 <!-- swagger --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</…...

Qt/C++学习系列之Excel使用记录

Qt/C学习系列之Excel使用记录 前言The process was ended forcefully.解决方式断点查语句问题 总结 前言 在项目中解析条目达50多条&#xff0c;并且都需要将对应的结果进行显示。为了将结果显示的更加清晰&#xff0c;考虑采用QTableWidget进行表格设置&#xff0c;而在使用过…...

【Kotlin】注解反射扩展

文章目录 注解用法反射类引用 扩展扩展函数的作用域成员方法优先级总高于扩展函数 被滥用的扩展函数扩展属性静态扩展 标准库中的扩展函数 使用 T.also 函数交换两个变量sNullOrEmpty | isNullOrBlankwith函数repeat函数 调度方式对扩展函数的影响静态与动态调度扩展函数始终静…...

固定ip和非固定ip的区别是什么?如何固定ip地址

在互联网中&#xff0c;我们常会接触到固定IP和非固定IP的概念。它们究竟有何不同&#xff1f;如何固定IP地址&#xff1f;让我们一起来探究这个问题。 一、固定IP和非固定IP的区别是什么 固定IP&#xff08;静态IP&#xff09;和非固定IP&#xff08;动态IP&#xff09;是两种…...