今天我们来谈谈Spring的内核之一——IoC容器

大家可能会有这样的疑问:

”这玩意为啥要叫容器呢?好奇怪“

”容器不是装东西的吗?难道IoC容器也是用来装什么东西的?“

有上述两个想法的小伙伴,我觉得你们是非常聪明的,并且平时有思考的习惯。别着急,让我们慢慢往下看

IoC容器的两大重要概念

IoC理解

​ IoC(Inverse of Control)控制反转,是应用本身不再进行对象的创建和维护工作,而是将这些工作交给外部容器去进行,这样控制权转移给外部容器,反转了。

举个例子:假设你和女朋友打算出去旅游,准备自驾去西藏玩。女朋友让你做旅游攻略,以及沿途行程安排。你本以为这是一件容易的事情,但实际操作后你抱怨道:“md,这比我该bug还麻烦”

刚好这时候,手机上推来一条“*西藏小团8人游,珠峰下的星空,只需要2XXX...*”的消息。这下爽了,研究一下直接报团,一下子省去了许多步骤,你开始幻想美丽的旅途...

思路回来一下,这个过程就很形象的描述了控制反转,你对旅游 (对象) 的控制权交给了旅行社 (容器) 。

DI理解

​ DI(Dependency Injection) 依赖注入指组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。 依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。

还拿上述例子举例,当你把你的旅游计划告诉给你好久不见,外地工作 的好朋友。你朋友听完之后也十分心动呀,说想带着他女朋友也一起去。

“那正好一起呀,人多大家玩的开心”。于是你把链接推给你的好朋友,让他也购买你这个旅行团,于是你们现在是一个旅行团,并且可以一起玩喽

你和你的朋友逻辑上是有关系的,可是不在一个地方,又想在一起玩。于是进入同一个旅行社(容器),当你们在西藏开始玩的时候(运行期),你们就可以见面了。

说白了,就是对象不用你创建了,他们之间的关系你也别管了。

所以:IoC容器 = IoC(控制反转)+DI(依赖注入)

这就是为什么spring框架在整个项目中有“大管家”的称号了

对于上面两个概念理解清楚了,其实就IoC容器你已经快“吃透”了。但是光有概念还不行,我们还得有细节和操作

创建IoC容器的方式

有以下两种常用的方式:

//创建IoC容器 利用ApplicationContext的子类ClassPathXmlApplicationContext(方式一)
ClassPathXmlApplicationContext ioc =
new ClassPathXmlApplicationContext("ioc.xml");
//方式二 利用ApplicationContext的子类FileSystemXmlApplicationContext
FileSystemXmlApplicationContext ioc2 =
new FileSystemXmlApplicationContext("D:\\code\\SpringDemo\\src\\main\\resources\\ioc.xml");

从代码中可以看出,主要区别在于配置文件.xml的路径上,绝对路径和项目路径

其实还有两种方式也可以用于IoC容器的创建:

  • AnnotationConfigApplicationContext

    直接加载上下文定义(仅限基于Java配置类使用)不使用XML文件,使用注解

  • XmlWebApplicationContext

    读取Web应用下的XML配置文件并加载上下文定义 用于web项目里

那么我们在深挖一下,上述两个子类中都有ApplicationContext的字样,那这个应该也是个类,是怎么来的呢?

下面代码模拟其中原理

//通过BeanFactory类创建ApplicationContext容器
// 加载配置文件
ClassPathResource resource = new ClassPathResource("applicationContext.xml"); // 创建BeanFactory容器
BeanFactory beanFactory = new XmlBeanFactory(resource); // 从容器中获取对象实例
MyBean myBean = (MyBean) beanFactory.getBean("myBean"); // 使用对象实例
myBean.doSomething();

原来是利用了BeanFactory这个类。其实ApplicationContext,它是 BeanFactory 的子类,更好的补充并实现了 BeanFactory 的。

网上查了资料,BeanFactory类似于Hashmap,键值对中分别装了对象的名称对象 (这里的对象被称为bean 马上讲到)但它一般只有 get, put 两个功能,所以称之为“低级容器”。

ApplicationContext 多了很多功能,因为它继承了多个接口,可称之为“高级容器”。

下面的图片可以很好的对他们的关系进行理解

装配bean

刚才提到了一个名称 bean ,我用“对象”一词对他进行转义。

其实bean就是包装好了的对象(Object),Java本身是面向对象编程语言,任何操作都是围绕作者对象进行,在这里,由spring对其进行封装。

那装配bean又是什么意思?有点像给枪上子弹的感觉

装配bean就是在IoC容器中创建bean 以及确定bean和bean之间的依赖关系

Spring支持三种装配bean的方式

  • 基于XML配置
  • 基于注解
  • 基于Java类配置

首先来看第一种,建立一个ioc.xml配置文件:

<!--创建Boy bean 相当于(Boy boy = new Boy())-->
<bean id="boy" class="ioc.Boy" /> <!--又创建Girl bean 相当于(Girl girl = new Girl)-->
<bean id="Girl" class="ioc.Girl"/>

第二种,通过注解:

依赖注入的方法

  • 方法注入(推荐)
  • 构造器注入
  • 接口注入

注意:spring只支持前两种方式

看看下面男孩和女孩配对的例子 :

package ioc;

public class Boy {
private String name;
private int age;
private Girl girl; public Boy() {
System.out.println("实例化boy");
} public Boy(String name, int age) {
this.name = name;
this.age = age;
} public Boy(Girl girl) {//构造器注入(方式一)
this.girl = girl;
} public Boy(String name, int age, Girl girl) {
this.name = name;
this.age = age;
this.girl = girl;
System.out.println("构造器注入");
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public Girl getGirl() {
return girl;
} public void setGirl(Girl girl) {//方法注入(方式二)
this.girl = girl;
System.out.println("方法注入girl");
} @Override
public String toString() {
return "Boy{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

上述例子中我们定义了一个男孩类,男孩的属性为name,age,Girl(配对的女孩) ,女孩类的定义如下:

package ioc;

public class Girl {
private String name;
private int age; public Girl() {
System.out.println("实例化girl");
} public Girl(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

传统来说我们会通过这种方式来设置属性和构造依赖关系

Boy boy = new Boy("张三",25,new Girl("小美",22))

但现在,我们会在ioc.xml文件中写(方法注入):

<bean id="Girl" class="ioc.Girl">
<!--相当于调用 setName("小美")-->
<property name = "name" value = "小美"/>
<property name = "age" value = "22"/>
</bean> <!--创建Boy bean 相当于(Boy boy = new Boy())-->
<!--init-method设置初始化值 destroy-method用于销毁ioc-->
<bean id="boy" class="ioc.Boy" >
<!--给类的属性设置初始值-->
<property name="name" value="张三"/>
<property name="age" value="25"/>
<!--依赖注入(方法注入) 相当于调用了setGirl(girl)-->
<property name="girl" ref="Girl" />
</bean>

上述的是方法注入,还有一种spring支持的 构造器注入 如下:

<bean id="Girl" class="ioc.Girl">
<!--相当于调用 setName("小美")-->
<property name = "name" value = "小美"/>
<property name = "age" value = "22"/>
</bean> <bean id="boy2" class="ioc.Boy" scope="prototype">
<constructor-arg value="李四"/>
<constructor-arg value="24"/>
<!--依赖注入(构造器注入)-->
<constructor-arg ref="Girl"/>
</bean>

注:构造器注入需要相应的构造器来进行

后续结果

通过上述的控制翻转和依赖注入,我们相当于完成了对象的设置,现在需要通过将我们输入的东西展示出来,代码如下:

package ioc;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext; public class TestIoC {
public static void main(String[] args) {
//创建IoC容器(方式一)
ClassPathXmlApplicationContext ioc =
new ClassPathXmlApplicationContext("ioc.xml"); //方式二
// FileSystemXmlApplicationContext ioc2 =
// new FileSystemXmlApplicationContext("D:\\code\\SpringDemo\\src\\main\\resources\\ioc.xml");
// System.out.println("ioc2"+ioc2); //获取IoC容器 的 Bean实例 (Boy对象实例)
/ Boy boy = (Boy) ioc.getBean("boy");
System.out.println("boy:"+boy); Boy boy2 = (Boy) ioc.getBean("boy2");
System.out.println("boy2:"+boy2);
System.out.println(boy2.getGirl()); }
}

打印结果:

boy:Boy{name='张三', age=25}
boy2:Boy{name='李四', age=24}
Girl{name='小美', age=22}

额外提一嘴,getBean()有两种用法:name: bean的id ;class:bean的类型(容器内唯一)

具体怎么玩呢?请听下回分解

IoC容器趣谈的更多相关文章

  1. 1.3浅谈Spring(IOC容器的实现)

    这一节我们来讨论IOC容器到底做了什么. 还是借用之前的那段代码 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationCon ...

  2. 深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

  3. 【最简单IOC容器实现】实现一个最简单的IOC容器

    前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...

  4. 深入理解DIP、IoC、DI以及IoC容器(转)

    深入理解DIP.IoC.DI以及IoC容器 摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.D ...

  5. 【转】深入理解DIP、IoC、DI以及IoC容器

    原文链接:http://www.cnblogs.com/liuhaorain/p/3747470.html 前言 对于大部分小菜来说,当听到大牛们高谈DIP.IoC.DI以及IoC容器等名词时,有没有 ...

  6. IoC容器的初始化过程

    1.简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动. 2.具体来说,这个启动包括BeanDefinition的Resource定位.载入和 ...

  7. DIP、IoC、DI以及IoC容器

    深入理解DIP.IoC.DI以及IoC容器 摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.D ...

  8. Spring技术内幕总结 - IoC容器的实现

    IoC:Inversion of Control,控制反转,即依赖对象的获得被反转了(DI:dependency inversion,依赖注入)在Spring中,IoC容器是实现这个模式的载体.它可以 ...

  9. 【来龙去脉系列】深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

  10. 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器

    1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...

随机推荐

  1. 2023牛客暑期多校训练营2 DEFGHIK

    比赛链接 D 题解 知识点:贪心. 首先,因为第一个人喜欢吃的可能会被后面的人选中,因此直接选最喜欢吃的可能会浪费机会.所以,我们考虑先看后面的人怎么选,就是倒着贪心,我们考虑证明. 假设当前剩下的菜 ...

  2. 策略模式+Spring配置类优化多if..else思路

    图示 1. 现状 场景: 假设设备上报不同类型的消息,我们要对不同类型的消息做不同的处理.如果我们通过if..else的方式处理的话会显得比较冗余. 例如: if("alarmEvent&q ...

  3. 【技术积累】Linux中的命令行【理论篇】【三】

    apt-get命令 命令介绍 Debian Linux发行版中的APT软件包管理工具,apt-get命令 是Debian Linux发行版中的APT软件包管理工具.所有基于Debian的发行都使用这个 ...

  4. 一款开源免费、更符合现代用户需求的论坛系统:vanilla

    对于个人建站来说,WordPress相信很多读者都知道了.但WordPress很多时候我们还是用来建立自主发布内容的站点为主,适用于个人博客.企业主站等.虽然有的主题可以把WordPress变为论坛, ...

  5. [postgresql]逻辑备份与还原

    前言 Postgres提供pg_dump和pg_dumpall用于数据库逻辑备份. pg_dumpall:将一个PostgreSQL数据库集群全部转储到一个脚本文件中 pg_dump:可以选择一个数据 ...

  6. CentOS7升级python3到最新版

    前言 最近在学习sanic,需要python3.7以上的版本,而centos7默认的python版本是3.6.8,所以升级了一下版本,在此笔录. 步骤 首先,从python官网下载最新版的python ...

  7. 微信的 h5 支付和 jsapi 支付

    目录 申请商户号 申请商户证书 设置APIv3密钥 下载 SDK 开发包 下载平台证书 关联 AppID 账号 开通 H5 支付 H5支付流程 开通 JSAPI 支付 JSAPI 支付流程 通用微信支 ...

  8. 《高级程序员 面试攻略 》RabbitMQ 如何实现可靠性

    RabbitMQ 提供了多种机制来实现消息传递的可靠性.下面是一些常见的方法: 1. 持久化消息:RabbitMQ 允许将消息标记为持久化,以确保即使在发生故障或重启后,消息也不会丢失.通过将消息的` ...

  9. 7、Spring之基于注解管理bean

    本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行. 7.1.环境搭建 创建名为spring_ioc_annotation的新module,过程参考3.1 ...

  10. 万字长文深度解读Java线程池,硬核源码分析

    前言 本文将深入分析Java线程池的源码,包括线程池的创建.任务提交.工作线程的执行和线程池的关闭等过程.通过对线程池源码的解析,我们能够更好地理解线程池的原理和机制,为我们在实际开发中合理使用线程池 ...