关于后端:从六个方面读懂IoC控制反转和DI依赖注入

41次阅读

共计 6471 个字符,预计需要花费 17 分钟才能阅读完成。

前言

在一开始学习 Spring 的时候,咱们就接触 IoC 了,作为 Spring 第一个最外围的概念,咱们在解读它源码之前肯定须要对其有深刻的意识,对于初学 Spring 的人来说,总感觉 IOC 是模糊不清的,是很难了解的,明天和大家分享网上的一些技术大牛们对 Spring 框架的 IOC 的了解以及谈谈我对 Spring IOC 的了解。

“管制反转”,不是什么技术

Ioc—Inversion of Control,即“管制反转”,不是什么技术,而是一种设计思维。在 Java 开发中,Ioc 意味着将你设计好的对象交给容器管制,而不是传统的在你的对象外部间接管制。如何了解好 Ioc 呢?了解好 Ioc 的要害是要明确“谁管制谁,管制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那咱们来深入分析一下:

我是这样了解 IOC 容器:以水桶为例,有大的,小的,有金的,银的,各种各样。IOC 容器也如此,每种不同的容器有本人性能,然而他们有一个是不能扭转的,那就是装水,咱们所学习的 IOC 容器也一样。

IOC 是 Inversion of Control 的缩写,因为引进了两头地位的第三方,也就是 IOC 容器使得没有关联关系的类有了一个独特的关系 – 被 ICO 容器所治理,所以说 IOC 容器起到了粘合剂的作用。

一、IOC 的思维

首先想说说 IoC(Inversion of Control,管制倒转)。这是 spring 的外围,贯通始终。所谓 IoC,对于 spring 框架来说,就是由 spring 来负责管制对象的生命周期和对象间的关系。这是什么意思呢,举个简略的例子,咱们是如何找女朋友的?常见的状况是,咱们到处去看哪里有长得丑陋身材又好的 mm,而后打听她们的兴趣爱好、qq 号、电话号、ip 号、iq 号………,想方法意识她们,投其所好送其所要,而后嘿嘿……这个过程是简单深奥的,咱们必须本人设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要应用另外的对象,就必须失去它(本人 new 一个,或者从 JNDI 中查问一个),应用完之后还要将对象销毁(比方 Connection 等),对象始终会和其余的接口或类藕合起来。

1.1ioc 的益处

IOC 的益处,初始化的过程中就不可避免的会写大量的 new,只须要保护 XML 或注解,不须要大量的批改代码,IOC 容器是分层的,从最底层 BeanFactory 网上找(前面源码解读会具体解说),实现类与类之间的解耦合,能够将代码拆散,每个人只须要写本人的局部,利于团队合作。

1.2、IoC 能做什么

IoC 不是一种技术,只是一种思维,一个重要的面向对象编程的法令,它能领导咱们如何设计出松耦合、更低劣的程序。传统应用程序都是由咱们在类外部被动创立依赖对象,从而导致类与类之间高耦合,难于测试;有了 IoC 容器后,把创立和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 涣散耦合,这样也不便测试,利于性能复用,更重要的是使得程序的整个体系结构变得非常灵活。..
《2020 最新 Java 根底精讲视频教程和学习路线!》

其实 IoC 对编程带来的最大扭转不是从代码上,而是从思维上,产生了“主从换位”的变动。应用程序本来是老大,要获取什么资源都是主动出击,然而在 IoC/DI 思维中,应用程序就变成被动的了,被动的期待 IoC 容器来创立并注入它所须要的资源了。

IoC 很好的体现了面向对象设计法令之一—— 好莱坞法令:“别找咱们,咱们找你”;即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象被动去找。

二、Spring IoC 总览

Spring 的 IoC 容器在实现管制反转和依赖注入的过程中, 能够划分为两个阶段:

  • 容器启动阶段
  • bean 实例化阶段

这两个阶段中,IoC 容器别离作了以下这些事件:

这里可能会齐全搞不懂下面这些货色是什么, 不过不要紧, 这里只是给大家一个根本的印象, 晓得 SpringIoC容器在实现管制反转和依赖注入性能的时候不是欲速不达的, 也分了两个阶段, 并且大抵对两个阶段所做的事件有一个印象, 上面我要对每一个阶段的每一项工作都进行深刻的解说, 请大家急躁的看上来.

三、IoC 和 DI

DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动静的将某个依赖关系注入到组件之中。依赖注入的目标并非为软件系统带来更多功能,而是为了晋升组件重用的频率,并为零碎搭建一个灵便、可扩大的平台。通过依赖注入机制,咱们只须要通过简略的配置,而无需任何代码就可指定指标须要的资源,实现本身的业务逻辑,而不须要关怀具体的资源来自何处,由谁实现。

了解 DI 的要害是:“谁依赖谁,为什么须要依赖,谁注入谁,注入了什么”,那咱们来深入分析一下:

●谁依赖于谁:当然是应用程序依赖于 IoC 容器;

●为什么须要依赖:应用程序须要 IoC 容器来提供对象须要的内部资源;

●谁注入谁:很显著是 IoC 容器注入应用程序某个对象,应用程序依赖的对象;

●注入了什么:就是注入某个对象所须要的内部资源(包含对象、资源、常量数据)。

3.1IoC 和 DI 由什么关系呢?

IoC 和 DI 由什么关系呢?其实它们是同一个概念的不同角度形容,因为管制反转概念比拟含混(可能只是了解为容器管制对象这一个层面,很难让人想到谁来保护对象关系),所以 2004 年大师级人物 Martin Fowler 又给出了一个新的名字:“依赖注入”,绝对 IoC 而言,“依赖注入”明确形容了“被注入对象依赖 IoC 容器配置依赖对象”。

看过很多对 Spring 的 Ioc 了解的文章,好多人对 Ioc 和 DI 的解释都艰涩难懂,反正就是一种说不清,道不明的感觉,读完之后仍然是一头雾水,感觉就是开涛这位技术牛人写得特地通俗易懂,他分明地解释了 IoC(管制反转) 和 DI(依赖注入)中的每一个字,读完之后给人一种恍然大悟的感觉。我置信对于初学 Spring 框架的人对 Ioc 的了解应该是有很大帮忙的。

四、分享 Bromon 的 blog 上对 IoC 与 DI 浅显易懂的解说

4.1、IoC(管制反转)

首先想说说 IoC(Inversion of Control,管制反转)。这是 spring 的外围,贯通始终。所谓 IoC,对于 spring 框架来说,就是由 spring 来负责管制对象的生命周期和对象间的关系。这是什么意思呢,举个简略的例子,咱们是如何找女朋友的?常见的状况是,咱们到处去看哪里有长得丑陋身材又好的 mm,而后打听她们的兴趣爱好、qq 号、电话号、ip 号、iq 号………,想方法意识她们,投其所好送其所要,而后嘿嘿……这个过程是简单深奥的,咱们必须本人设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要应用另外的对象,就必须失去它(本人 new 一个,或者从 JNDI 中查问一个),应用完之后还要将对象销毁(比方 Connection 等),对象始终会和其余的接口或类藕合起来。

那么 IoC 是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介治理了很多男男女女的材料,我能够向婚介提出一个列表,通知它我想找个什么样的女朋友,比方长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,而后婚介就会依照咱们的要求,提供一个 mm,咱们只须要去和她谈恋爱、结婚就行了。简单明了,如果婚介给咱们的人选不符合要求,咱们就会抛出异样。整个过程不再由我本人管制,而是有婚介这样一个相似容器的机构来管制。Spring 所提倡的开发方式就是如此,所有的类都会在 spring 容器中注销,通知 spring 你是个什么货色,你须要什么货色,而后 spring 会在零碎运行到适当的时候,把你要的货色被动给你,同时也把你交给其余须要你的货色。所有的类的创立、销毁都由 spring 来管制,也就是说管制对象生存周期的不再是援用它的对象,而是 spring。对于某个具体的对象而言,以前是它管制其余对象,当初是所有对象都被 spring 管制,所以这叫管制反转。

4.2、DI(依赖注入)

IoC 的一个重点是在零碎运行中,动静的向某个对象提供它所须要的其余对象。这一点是通过 DI(Dependency Injection,依赖注入)来实现的。比方对象 A 须要操作数据库,以前咱们总是要在 A 中本人编写代码来取得一个 Connection 对象,有了 spring 咱们就只须要通知 spring,A 中须要一个 Connection,至于这个 Connection 怎么结构,何时结构,A 不须要晓得。在零碎运行时,spring 会在适当的时候制作一个 Connection,而后像打针一样,注射到 A 当中,这样就实现了对各个对象之间关系的管制。A 须要依赖 Connection 能力失常运行,而这个 Connection 是由 spring 注入到 A 中的,依赖注入的名字就这么来的。那么 DI 是如何实现的呢?Java 1.3 之后一个重要特色是反射(reflection),它容许程序在运行的时候动静的生成对象、执行对象的办法、扭转对象的属性,spring 就是通过反射来实现注入的。

了解了 IoC 和 DI 的概念后,所有都将变得简单明了,剩下的工作只是在 spring 的框架中沉积木而已。

五、我对 IoC(管制反转)和 DI(依赖注入)的了解

在平时的 java 利用开发中,咱们要实现某一个性能或者说是实现某个业务逻辑时至多须要两个或以上的对象来合作实现,在没有应用 Spring 的时候,每个对象在须要应用他的单干对象时,本人均要应用像 new object() 这样的语法来将单干对象创立进去,这个单干对象是由本人被动创立进去的,创立单干对象的主动权在本人手上,本人须要哪个单干对象,就被动去创立,创立单干对象的主动权和创立机会是由本人把控的,而这样就会使得对象间的耦合度高了,A 对象须要应用单干对象 B 来共同完成一件事,A 要应用 B,那么 A 就对 B 产生了依赖,也就是 A 和 B 之间存在一种耦合关系,并且是严密耦合在一起,而应用了 Spring 之后就不一样了,创立单干对象 B 的工作是由 Spring 来做的,Spring 创立好 B 对象,而后存储到一个容器外面,当 A 对象须要应用 B 对象时,Spring 就从寄存对象的那个容器外面取出 A 要应用的那个 B 对象,而后交给 A 对象应用,至于 Spring 是如何创立那个对象,以及什么时候创立好对象的,A 对象不须要关怀这些细节问题(你是什么时候生的,怎么生进去的我可不关怀,能帮我干活就行),A 失去 Spring 给咱们的对象之后,两个人一起合作实现要实现的工作即可。

所以管制反转 IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创立机会是由本人把控的,而当初这种势力转移到第三方,比方转移交给了 IoC 容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC 容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖 IoC 容器了,通过 IoC 容器来建设它们之间的关系。

这是我对 Spring 的 IoC(管制反转)的了解。DI(依赖注入)其实就是 IOC 的另外一种说法,DI 是由 Martin Fowler 在 2004 年初的一篇论文中首次提出的。他总结:管制的什么被反转了?就是:取得依赖对象的形式反转了。

六、容器启动阶段的解说

6.1、IOC 的技术实现形式

“伙计,来杯啤酒!”当你来到酒吧,想要喝杯啤酒的时候,通常会间接招呼服务生,让他为你 送来一杯清凉解渴的啤酒。同样地,作为被注入对象,要想让 IoC 容器为其提供服务,并 将所须要的被依赖对象送过来,也须要通过某种形式告诉对方。

  • 如果你是酒吧的常客,或者你刚坐好,服务生曾经将你最常喝的啤酒放到了你背后
  • 如果你是首次或偶然光顾,兴许你坐下之后还要招呼服务生,“Waiter,Tsingdao, please.”
  • 还有一种可能,你基本就不晓得哪个牌子是哪个牌子,这时,你只能打手势或罗唆画出商标
    图来通知服务生你到底想要什么了吧!

不管怎样,你终究会找到一种形式来向服务生表白你的需要,以便他为你提供适当的服务。那么,在 IoC 模式中,被注入对象又是通过哪些形式来告诉 IoC 容器为其提供适当服务的呢? 罕用的有两种形式:构造方法注入和 setter 办法注入,还有一种曾经退出历史舞台的接口注入形式,上面就比拟一下三种注入形式:

  • 接口注入。从注入形式的应用上来说,接口注入是当初不甚提倡的一种形式,根本处于“服役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和 setter 办法注入则不须要如此。
  • 构造方法注入。这种注入形式的长处就是,对象在结构实现之后,即已进入就绪状态,能够马上应用。毛病就是,当依赖对象比拟多的时候,构造方法的参数列表会比拟长。而通过反射结构对象的时候,对雷同类型的参数的解决会比拟艰难,保护和应用上也比拟麻烦。而且在 Java 中,构造方法无奈被继承,无奈设置默认值。对于非必须的依赖解决,可能须要引入多个构造方法,而参数数量的变动可能造成保护上的不便。
  • setter 办法注入。因为办法能够命名,所以 setter 办法注入在描述性上要比构造方法注入好一些。另外,setter 办法能够被继承,容许设置默认值,而且有良好的 IDE 反对。毛病当然就是对象无奈在结构实现后马上进入就绪状态。

其实,这些操作都是由 IoC 容器来做的,咱们所要做的,就是调用 IoC 容器来取得对象而已。

6.2、IoC 容器及 IoC 容器如何获取对象间的依赖关系

Spring 中提供了两种 IoC 容器:

  • BeanFactory
  • ApplicationContext

这两个容器间的关系如下图:

咱们能够看到,ApplicationContext 是 BeanFactory 的子类,所以,ApplicationContext 能够看做更弱小的 BeanFactory,他们两个之间的区别如下:

  • BeanFactory。根底类型 IoC 容器,提供残缺的 IoC 服务反对。如果没有非凡指定,默认采纳提早初始化策略(lazy-load)。只有当客户端对象须要拜访容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所须要的资源无限。对于资源无限,并且性能要求不是很严格的场景,BeanFactory 是比拟适合的 IoC 容器抉择。
  • ApplicationContext。ApplicationContext 在 BeanFactory 的根底上构建,是绝对比拟高级的容器实现,除了领有 BeanFactory 的所有反对,ApplicationContext 还提供了其余高级个性,比方事件公布、国际化信息反对等,ApplicationContext 所治理的对象,在该类型容器启动之后,默认全副初始化并绑定实现。所以,绝对于 BeanFactory 来说,ApplicationContext 要求更多的系统资源,同时,因为在启动时就实现所有初始化,容 器启动工夫较之 BeanFactory 也会长一些。在那些系统资源短缺,并且要求更多功能的场景中,ApplicationContext 类型的容器是比拟适合的抉择。

然而咱们无论应用哪个容器,咱们都须要通过某种办法通知容器对于对象依赖的信息,只有这样,容器能力正当的发明出对象,否则,容器本人也不晓得哪个对象依赖哪个对象,如果胡乱注入,那不是发明出一个四不像。实践上将咱们能够通过任何形式来通知容器对象依赖的信息,比方咱们能够通过语音通知他,然而并没有人实现这样的代码,所以咱们还是老老实实应用 Spring 提供的办法吧:

  • 通过最根本的文本文件来记录被注入对象和其依赖对象之间的对应关系
  • 通过描述性较强的 XML 文件格式来记录对应信息
  • 通过编写代码的形式来注册这些对应信息
  • 通过注解形式来注册这些对应信息

尽管提供了四种形式,然而咱们个别只应用 xml 文件形式和注解形式,所以,就重点解说这两种形式。

文章到这里就完结了

链接:https://juejin.cn/post/690185…

正文完
 0