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,一经查实,立即删除!

相关文章

全量知识系统 详细设计 之preserving (百度AI助手QA)

Q1. 1、三种对象三种库&#xff1a;python 数据对象 none对象: 对等立方体&#xff08;PQR&#xff09; ole对象Y 。 pqr均为立体库&#xff08;工厂级的&#xff09;&#xff1b;Java 类对象 Empty对象 &#xff1a;公差立方体(XYZ ) 值对象C。 xyz均为线库&#xff08;车间…

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

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

Java language programming:求学生的平均身高

题目&#xff1a; 从键盘输入若干个人的身高&#xff0c;计算出平均身高&#xff08;平均身高为int类型&#xff09;。当输入身高值为0时&#xff0c;表示输入结束。 输入格式: 输入若干个人的身高值输入在同一行&#xff0c;用空格分隔。 输出格式: 按照输出样例格式输出平均…

共享内存 - windows平台和linux平台的不同处理

1.各个平台下的共享内存的使用方法 共享内存主要用来进行模块分割&#xff0c;复杂性隔离的用途。python这类跨平台平台其实是无法完全消弭各个平台间的个体差异&#xff0c;一个简单的例子&#xff1a; import posix_ipc import mmap# 创建或打开共享内存 shm posix_ipc.Sh…

matlab 坐标系变换

文章目录 对象轴角 axis-angles欧拉角 Euler angles四元数 quaternion旋转矩阵 rotation matrices齐次变换矩阵 Homogeneous Transformation坐标系换算 Coordinate Conversions图变换 Plot Transformations对象 对象含义se2SE(2)齐次变换矩阵se3SE(3)齐次变换矩阵so2SO(2)旋转s…

内存泄漏检查工具下载(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&…

面向对象设计之开闭原则

设计模式专栏&#xff1a; http://t.csdnimg.cn/4Mt4u 目录 1.引言 2.如何理解“对扩展开放、对修改关闭” 3.修改代码就意味着违反开闭原则吗 4.如何做到“对扩展开放、对修改关闭” 5.如何在项目中灵活应用开闭原则 6.总结 1.引言 开闭原则(Open Closed Principle&…

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

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

【RV1126】瑞芯微RV1126开发板双网口问题

今天新拿到瑞芯微RV1126的开发板&#xff0c;是双网口双百兆接口&#xff0c;内存为2G&#xff0c;emmc flash为32G&#xff0c;一开始无法分辨物理接口和系统的eth是对应的&#xff0c;遂想用ethtool工具来查看网速&#xff0c;发现&#xff1a; ethtool eth0 # 可以正常返回…

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

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

如何在 Debian VPS 上添加、删除和授予用户 sudo 权限

简介 当你启动一个新的服务器时&#xff0c;会创建一个名为 root 的默认账户。这个用户拥有完全的系统访问权限&#xff0c;应该仅用于管理任务。作为 root 用户&#xff0c;你基本上可以对系统做任何操作&#xff0c;这很强大&#xff0c;但也极其危险。Linux 没有“撤销”按…

Leetcode 73 矩阵置零

题目信息 LeetCode地址: . - 力扣&#xff08;LeetCode&#xff09; 题目理解 矩阵是m*n的&#xff0c;如果某个元素(i,j)等于0&#xff0c;则将第i行和第j列的所有元素都置零。既然如此&#xff0c;我们可以便利每一个元素&#xff0c;并记录下哪一行哪一列有零。记录完毕后…

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

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

KaTex 常用公式编辑

原文&#xff1a;https://blog.iyatt.com/?p7854 注&#xff1a;语法上和 Latex 差不多一样&#xff0c;我是因为 WordPress 上使用 WP Githuber MD 插件&#xff0c;才用的 KaTex&#xff08;插件里面的 LaTex 模块有 bug&#xff0c;无法渲染&#xff09; 希腊字母 大写代…

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; *…