SSM框架学习——Spring的控制反转IoC与依赖注入DI

控制反转IoC与依赖注入DI

概念

提到Spring首先想到的肯定是Spring的IoC容器了。在了解Spring的用法之前我们必须了解什么是控制反转IoC依赖注入DI

**控制反转(Inversion of Control)**是面向对象编程中的一种设计原则,它建议将不需要的职责移出类,让类专注于核心职责,从而提供松散耦合,提高优化软件程序设计。

简单一点来说,我原来需要一个对象需要自己手动去new,我必须知道哪些类实现了相应的接口,而有了控制反转,我只需要向框架的容器要一个,由它实现装配,对对象组件的控制权也就由代码转移到了外部容器。

控制反转有多种实现方式:

  • 依赖注入(Dependency Injection)
  • 依赖查找(Dependency Lookup)

其中依赖查找又可以分为依赖拖拽上下文依赖查找

依赖注入

我们这里主要来探讨依赖注入,它的基本原则是应用组件不应该负责查找资源或者其他依赖对象,配置对象的工作由IoC容器负责,即组件不做定位查询,只提供常规的Java方法让容器去决定依赖关系。

Spring实现DI有四种方式:构造器注入、setter注入、接口注入和属性注入。接口注入Spring3.x的特性,Spring4.x已经被废弃掉了,我们就着重探讨另外三者。

我们还是在原来的test1项目上进行演示。首先,我们创建一个接口,其名称ITest

我们在接口中声明一个方法void say()

package top.cairbin.test1;public interface ITest {void say();
}

接下来我们定义一个实现ITest接口的类Test。这里不给出操作图片,请仿照之前的自行创建一个Test.java文件,下文同理。

package top.cairbin.test1;public class Test implements ITest{@Overridepublic void say() {System.out.println("Hi");}
}

我们创建一个调用者类User,它需要一个实现了ITest接口的对象。

package top.cairbin.test1;public class User {private ITest test;public void testSay() {test.say();}
}

我们尝试在App中去实例化这个对象并调用void testSay()这个方法。

package top.cairbin.test1;public class App 
{public static void main( String[] args ){User user = new User();user.testSay();}
}

接下来运行,发现了一堆报错。这很显然,因为我们没有给定User类中对应ITest类型的属性的实现。

我们接下来尝试使用依赖注入来解决这个报错。

先创建一个与src平级的文件夹,名称叫做resources

然后对这个文件夹右键,点击Build Path里的Use as Source Folder

我们要告诉IoC容器谁实现了ITest接口,于是我们在resources目录下创建一个AppCtx.xml文件。

在文件中写入如下内容

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><bean id="test" class="top.cairbin.test1.Test"></bean></beans>

我们还要告诉Spring去加载这个xml配置文件,于是在App类中(注意新引入的两个包)

package top.cairbin.test1;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App 
{public static void main( String[] args ){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("AppCtx.xml");User user = (User)applicationContext.getBean("user");user.testSay();}
}

构造器注入

构造器注入是SpringFramework推荐的一种方式,它要求提供有参数的构造方法。

我们对类User进行改造

package top.cairbin.test1;public class User {private final ITest test;public User(ITest test) {this.test = test;}public void testSay() {test.say();}
}

然后修改AppCtx.xml,告诉Spring类User构造器对应的参数

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><bean id="test" class="top.cairbin.test1.Test"></bean><bean id="user" class="top.cairbin.test1.User"><constructor-arg ref="test"></constructor-arg></bean></beans>

在这个xml文件中,我们告诉Spring类User构造器所需参数类型。这里的user对应top.cairbin.test1.User,而且与Main函数中的getBean("user")参数一致。

点击运行,发现控制台能够正常输出了

Setter注入

User内容

package top.cairbin.test1;public class User {private ITest test;public void setTest(ITest test) {this.test = test;}public void testSay() {test.say();}}

AppCtx.xml内容

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><bean id="test" class="top.cairbin.test1.Test"></bean><bean id="user" class="top.cairbin.test1.User"><property name="test" ref="test"/></bean></beans>

属性注入

属性注入,又或者说是字段注入,实际上并不是新的注入类型,它实际上通过注解实现,底层利用反射机制来设置值。

我们在AppCtx.xml文件中修改内容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><bean id="test" class="top.cairbin.test1.Test"></bean><bean id="user" class="top.cairbin.test1.User" autowire="byName"></bean></beans>

然后修改User类,给属性加一个注解@Autowired并设置一个public void setTest(ITest test)方法。

package top.cairbin.test1;import org.springframework.beans.factory.annotation.Autowired;public class User {@Autowiredprivate ITest test;public void setTest(ITest test) {this.test = test;}public void testSay() {test.say();}
}

点击运行按钮我们能看到控制台能够正常输出。

属性注入与自动扫描

我们想扔掉讨厌的setter方法,并且不再写这么麻烦的XML文件,有一个好方法那就是自动扫描

我们修改AppCtx.xml里开启自动扫描。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><context:component-scan base-package="top.cairbin.test1" />
</beans>

注意这里的base-package属性,为你要扫描的包!!!

然后将Test类注册为Bean,实际上就添加一个@Component注解

@Component
public class Test implements ITest{@Overridepublic void say() {System.out.println("Hi");}
}

同理,在User类上也添加一个@Component注解,在需要注入的字段上添加@Autowired注解

package top.cairbin.test1;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class User {@Autowiredprivate ITest test;public void testSay() {test.say();}
}

保存后,点击运行就能看到控制台输出。

这里对@Component注解进行说明,它就相当于我们在AppCtx.xml里添加了一个<bean></bean>标签,其id为首字母小写的类名,例如类名为TestDi,则对应id就是testDi

如果你想要自定义这个id,只需要这样写注解即可@Component("testDi")

属性注入与其它注入混合使用

问题来了,当属性注入和其它注入方式混合使用会怎么样。

在Spring4.3之前,需要添加@Autowired注解在构造函数上;在Spring4.3之后,如果只存在一个构造函数,则不用添加@Autowired,如果存在多个构造函数,则必须指定哪个来注入以来项,即在上面添加@Autowired

当属性注入与Setter注入同时使用时,例如下方代码:

public class Example {@Autowiredprivate Bean1 bean1;@Autowiredpublic void setBean1(Bean1 bean1) {this.bean1 = bean1;}  
}

Spring会优先使用Setter注入!!!

上述只是举例,在实际项目编写不要使用混合的注入方式,因为这会降低代码可读性!!

对比几种DI的方式

构造器注入

对于构造器注入,类的依赖关系在构造器中很明显,所有的依赖项在构造方法中,所以所有的依赖项都第一时间被注入到类中,且无法更改,即构造的对象是不可变的。

为什么Spring推荐这种方式?因为它可以使代码更健壮,可以防止空指针异常。

但是构造器注入也并不完美,它缺乏灵活性,不可能更改对象的依赖关系,在重构代码的时候比较麻烦。甚至有可能产生循环依赖。

Setter注入

Setter注入是一种灵活的方式,但是在多线程环境中可能变得不安全。另外Setter注入需要进行Null检查,否则可能报错,这影响了代码的健壮性。

属性注入

快速方便,与IoC容器耦合,但会带来额外的性能开销和兼容问题,同时也打破了封装这一特性,不利于面向对象编程。同时它也可能存在空指针异常,不利于代码的健壮性。

结束

本文到此结束,看完这一章你对依赖注入应该有了一个大致的了解,但是也应该会有很多疑问。Bean是什么?注解到底帮助我们实现了什么?我们将在下一节来探究这个问题。

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

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

相关文章

【软考】系统集成项目管理工程师(二十二)法律法规【2分】

一、合同法 1、内容 当事人的名称或者姓名和住所、标的、数量、质量、价款或者报酬&#xff1b;履行期限、地点和方式&#xff1b;违约责任和解决争议的方法 练一练 【例1-高16下】格式条款是当事人为了重复使用而预先拟定&#xff0c;并在订立合同时未与对方协商的…

内存泄漏检查工具下载(vld)

前言&#xff1a;在我们向内存申请动态空间的时候&#xff0c;如果使用完之后不将申请的空间释放&#xff0c;就会造成内存泄漏的情况&#xff0c;但是一般情况下&#xff0c;我们是无法通过运行代码来知道是否造成了内存泄漏&#xff0c;所以vld就成为了检查内存是否泄漏的好帮…

rocketMQ中store目录解释

rocketMQ中有个store目录&#xff0c;它是用来存放和管理broker中的消息&#xff0c;queue和topic的。 下面的是store的目录 abort&#xff1a;该文件在broker启动后会自动创建&#xff0c;broker关闭后消失&#xff0c;若没有消失则说明该broker非正常关闭。 checkpoint&…

Python多任务处理---多线程

引入 生活中&#xff0c;我们在电脑上打开了一个word, 这个word对操作系统来说就是一个进程。我们在进行word操作的时候&#xff0c;比如在你打字的时候&#xff0c;该word同时可以进行文字检查。发现了没&#xff0c;在同一个进程中&#xff0c;我们也可以进行同时操作。…

Acwing.731 毕业旅行问题(状态压缩动态规划)

题目 小明目前在做一份毕业旅行的规划。 打算从北京出发&#xff0c;分别去若干个城市&#xff0c;然后再回到北京&#xff0c;每个城市之间均乘坐高铁&#xff0c;且每个城市只去一次。 由于经费有限&#xff0c;小明希望能够通过合理的路线安排尽可能的省些路上的花销。 …

【C++进阶】二叉搜索树(来自二叉树的复仇)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 [本节目标] 1. 二叉搜索树…

MyBatis 参数重复打印的bug

现象 最近有个需求&#xff0c;需要在mybatis对数据库进行写入操作的时候&#xff0c;根据条件对对象中的某个值进行置空&#xff0c;然后再进行写入&#xff0c;这样数据库中的值就会为空了。 根据网上查看的资料&#xff0c;选择在 StatementHandler 类执行 update 的时候进…

C++之调用Python

1、配置头文件 Python安装目录下的include目录加入头文件目录。Visual Studio2022中操作路径是&#xff1a;属性–> C/C -> 常规-> 附加包含目录 C:\Users \AppData\Local\Programs\Python\Python39\include 2、配置lib库目录 要将Python39.lib加入编译链接。Visua…

neo4j使用详解(七、cypher数学函数语法——最全参考)

Neo4j系列导航&#xff1a; neo4j及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 5.数学函数 5.1.数值函数 数学函数仅对数字表达式进行运算&#xff0c;如果对任何其他值使用&#xff0c;将返回错误 abs()&#xf…

Nginx 基础

文章目录 Nginx概念安装下载上传安装包执行准备条件指定安装位置编译和安装启动服务创建启动脚本 linux文件目录nginx运行原理nginx配置域名概念和原理域名配置 Nginx 概念 Nginx 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是…

【Java八股面试系列】Arraylist和HashMap的底层原理

文章目录 ArrayList源码总&#xff1a;构造方法扩容机制remove HashMap总&#xff1a;构造方法细节问题putVal()方法resize()方法Hash值 HashMap常见问题 ConcurrentHashMap总&#xff1a;putVal()方法自己的测试 为什么重写HashCode和equals ArrayList源码 总&#xff1a; *…

3.28号arm

can总线相关理论 1. 概念 控制器局域网&#xff08;Controller Area Network&#xff0c;CAN&#xff09;&#xff0c;其特点是可拓展性好&#xff0c;可承受大量数据的高速通信&#xff0c;高度稳定可靠&#xff0c;因此常应用于汽车电子领域、工业自动化、医疗设备等高要求…

Java JSON字符串相关问题

一、依赖包 <!--json包--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.15</version></dependency> 二、举例 1.实体对象转Json字符串 1.1 代码实现 Dog.java: pack…

python_web1(前端开发之HTML、CSS、Bootstap、Javascript、JQuery)

文章目录 一、Flask网页开发1.1创建一个名为web1.py的python文件1.2 templates目录创建文件index.html 二、html标签2.1 编码2.2title < head >2.3 标题< h>2.4 div和span2.5超链接1.在index.xml文件中补充。2.修改web1.py文件3.添加get_self.html4.效果 2.6图片1.…

Java 堆外内存及调优

文章目录 直接内存简介为什么DirectByteBuffer可以优化 IO 性能 直接内存的分配直接内存的回收直接内存跟踪与诊断 直接内存简介 直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分&#xff0c;并非Java虚拟机规范中定义的内存区域。但是这部分内存的频繁使用&#x…

【LeetCode】三月题解

文章目录 [2369. 检查数组是否存在有效划分](https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/)思路&#xff1a;代码&#xff1a; [1976. 到达目的地的方案数](https://leetcode.cn/problems/number-of-ways-to-arrive-at-destination/) 思路…

C++教学——从入门到精通 5.单精度实数float

众所周知&#xff0c;三角形的面积公式是(底*高)/2 那就来做个三角形面积计算器吧 到吗如下 #include"bits/stdc.h" using namespace std; int main(){int a,b;cin>>a>>b;cout<<(a*b)/2; } 这不对呀&#xff0c;明明是7.5而他却是7&#xff0c;…

让IIS支持.NET Web Api PUT和DELETE请求

前言 有很长一段时间没有使用过IIS来托管应用了&#xff0c;今天用IIS来托管一个比较老的.NET Fx4.6的项目。发布到线上后居然一直调用不同本地却一直是正常的&#xff0c;关键是POST和GET请求都是正常的&#xff0c;只有PUT和DELETE请求是有问题的。经过一番思考忽然想起来了I…

YOLOv9改进策略 :主干优化 | 极简的神经网络VanillaBlock 实现涨点 |华为诺亚 VanillaNet

💡💡💡本文改进内容: VanillaNet,是一种设计优雅的神经网络架构, 通过避免高深度、shortcuts和自注意力等复杂操作,VanillaNet 简洁明了但功能强大。 💡💡💡引入VanillaBlock GFLOPs从原始的238.9降低至 165.0 ,保持轻量级的同时在多个数据集验证能够高效涨点…

每日学习笔记:C++ STL算法分类

非更易型 更易型 移除型 变序型 排序型 已排序区间算法 数值型算法