Spring 的本质系列之依赖注入

前言: Spring 这个轻量级的框架已经成为Web开发事实上的标准,阅读本篇文章之前希望你对OO,设计模式,单元测试,XML,反射等技术有一定了解。

概念:什么是IOC?

IoC(Inversion of Control),意为控制反转,不是什么技术,而是一种设计思想。Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制

如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  • 谁控制谁,控制什么:传统Java程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

1.对象的创建

面向对象的编程语言是用类(Class)来对现实世界进行抽象,在运行时这些类会生成对象(Object)。

当然,单独的一个或几个对象根本没办法完成复杂的业务, 实际的系统是由千千万万个对象组成的, 这些对象需要互相协作才能干活,例如对象A调用对象B的方法,那必然会提出一个问题:对象A怎么才能获得对象B的引用呢?

最简单的办法无非是: 当对象A需要使用对象B的时候,把它给new 出来 ,这也是最常用的办法,java 不就是这么做的?例如:

Apple a = new Apple();

后来业务变复杂了, 抽象出了一个水果(Fruit)的类, 创建对象会变成这个样子:

Fruit f1 = new Apple();Fruit f2 = new Banana();Fruit f3 = ......

很自然的,类似下面的代码就会出现:

这样的代码如果散落在各处,维护起来将会痛苦不堪, 例如你新加一个水果的类型Orange, 那得找到系统中所有的这些创建Fruit的地方,进行修改, 这绝对是一场噩梦。

解决办法也很简单,前辈们早就总结好了:工厂模式

工厂模式,以及其他模式像抽象工厂, Builder模式提供的都是创建对象的方法。

这背后体现的都是“封装变化”的思想。

这些模式只是一些最佳实践而已: 起了一个名称、描述一下解决的问题、使用的范围和场景,码农们在项目中还得自己去编码实现他们。

2.解除依赖

我们再来看一个稍微复杂一点,更加贴近实际项目的例子:

一个订单处理类,它会被定时调用:查询数据库中订单的处理情况,必要时给下订单的用户发信。

看起来也没什么难度, 需要注意的是很多类一起协作了, 尤其是OrderProcessor , 它依赖于OrderService 和 EmailService这两个服务,它获取依赖的方式就是通过单例方法。

如果你想对这个process方法进行单元测试--这也是很多优秀的团队要求的-- 麻烦就来了。

首先OrderService 确实会从真正的数据库中取得Order信息,你需要确保数据库中有数据, 数据库连接没问题,实际上如果数据库连接Container(例如Tomcat)管理的, 你没有Tomcat很难建立数据库连接。

其次这个EmailService 真的会对外发邮件, 你可不想对真正的用户发测试邮件,当然你可以修改数据库,把邮件地址改成假的,但那样很麻烦, 并且EmailService 会抛出一堆错误来,很不爽。

所有的这些障碍,最终会导致脆弱的单元测试: 速度慢, 不可重复,需要手工干预,不能独立运行。

想克服这些障碍,一个可行的办法就是不在方法中直接调用OrderService和EmailService的getInstance()方法, 而是把他们通过setter方法传进来。

通过这种方式,你的单元测试就可以构造一个假的OrderService 和假的EmailService 了。

例如OrderService 的冒牌货可以是MockOrderService , 它可以返回你想要的任何Order 对象, 而不是从数据库取。

MockEmailService 也不会真的发邮件, 而是把代码中试图发的邮件保存下来, 测试程序可以检查是否正确。

你的测试代码可能是这样的:

当然, 有经验的你马上就会意识到:需要把OrderService 和 EmailService 变成 接口或者抽象类, 这样才可以把Mock对象传进来。

这其实也遵循了面向对象编程的另外一个要求:对接口编程,而不是对实现编程。

3.Spring 依赖注入

啰啰嗦嗦说了这么多,快要和Spring扯上关系了。

上面的代码其实就是实现了一个依赖的注入,把两个冒牌货注入到业务类中(通过set方法), 这个注入的过程是在一个测试类中通过代码完成的。

既然能把冒牌货注入进去,那毫无疑问肯定也能把一个正经的类安插进去,因为setter 方法接受的是接口,而不是具体类。

用这种方式来处理对象之间的依赖,会强迫你对接口编程,好处显而易见。

随着系统复杂度的增长,这样的代码会越来越多,最后也会变得难于维护。

能不能把各个类之间的依赖关系统一维护呢?能不能把系统做的更加灵活一点,用声明的方式而不是用代码的方式来描述依赖关系呢?

肯定可以,在Java 世界里,如果想描述各种逻辑关系,XML是不二之选:

这个xml 挺容易理解的,但是仅仅有它还不够,还缺一个解析器(假设叫做XmlAppContext)来解析,处理这个文件,基本过程是:

  1. 解析xml, 获取各种元素
  2. 通过Java反射把各个bean 的实例创建起来:com.coderising.OrderProcessor, OrderServiceImpl, EmailServiceImpl.
  3. 还是通过Java反射调用OrderProcessor的两个方法:setOrderService(....) 和 setEmailService(...) 把orderService, emailService 实例 注入进去。

应用程序使用起来就简单了:

XmlAppContext ctx = new XmlAppContext("c:bean.xml");OrderProcessor op = (OrderProcessor) ctx.getBean("order-processor");op.process();

其实Spring的处理方式和上面说的非常类似,当然Spring 处理了更多的细节,例如不仅仅是setter方法注入, 还可以构造函数注入,init 方法,destroy方法等等,基本思想是一致的。

既然对象的创建过程和装配过程都是Spring做的,那Spring 在这个过程中就可以玩很多把戏了, 比如对你的业务类做点字节码级别的增强,搞点AOP什么的,这都不在话下了。

4.IoC vs DI

“不要给我们打电话,我们会打给你的(don‘t call us, we‘ll call you)”这是著名的好莱坞原则。

在好莱坞,把简历递交给演艺公司后就只有回家等待。由演艺公司对整个娱乐项目完全控制,演员只能被动式的接受公司的差使,在需要的环节中,完成自己的演出。

这和软件开发有一定的相似性,演员们就像一个个Java Object, 最早的时候自己去创建自己所依赖的对象, 有了演艺公司(Spring容器)的介入,所有的依赖关系都是演艺公司搞定的,于是控制就翻转了Inversion of Control, 简称IoC。

但是IoC这个词不能让人更加直观和清晰的理解背后所代表的含义,于是Martin Flower先生就创造了一个新词 : 依赖注入 (Dependency Injection,简称DI), 是不是更加贴切一点?

总结

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

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

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

对于Spring Ioc这个核心概念,我相信每一个学习Spring的人都会有自己的理解。这种概念上的理解没有绝对的标准答案,仁者见仁智者见智。理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在框架中堆积木而已。


本文转载自“码农翻身”公众号

【感谢您能看完,如果能够帮到您,麻烦点个赞~】

更多经验技术欢迎前来共同学习交流:一点课堂-为梦想而奋斗的在线学习平台 http://www.yidiankt.com/

![关注公众号,回复“1”免费领取-【java核心知识点】]

QQ讨论群:616683098

QQ:3184402434

想要深入学习的同学们可以加我QQ一起学习讨论~还有全套资源分享,经验探讨,等你哦!

控制反转和依赖注入的理解(通俗易懂)_Spring 的本质系列之依赖注入_一点课堂(多岸学院)...相关推荐

  1. PHP控制反转和依赖注入的理解(通俗易懂)

    目录 1.IoC是什么 2.IoC能做什么 3.IoC和DI 4.IoC(控制反转) 5.DI(依赖注入) 6.我对IoC(控制反转)和DI(依赖注入)的理解 学习PHP各个框架的过程中,都会听过Io ...

  2. 简述控制反转ioc_讲一下你理解的 DI 、IoC、DIP ?

    作者 | 木小楠 链接 |cnblogs.com/liuhaorain/p/3747470.html 摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要 ...

  3. MYSQLg高级-----SQL注入的理解(初级篇)以及如何防止注入

    SQL注入 1.SQL 注入概念 1.1 SQL注入是什么? 1.2 SQL注入定义 1.3 SQL注入特点 1.广泛性 2.隐蔽性 3.危害大 4.操作方便 1.4 SQl注入原理 1.5 SQl注 ...

  4. 控制反转---依赖注入理解

    在学习Spring的时候,意外找到这个控制反转(IoC)和面向切面(AOP)的容器框架之前,我们先来看一下什么是控制反转(IoC). 控制反转(Ioc)和依赖注入(DI)刚听到感到很难理解,平时也程序 ...

  5. 如何理解控制反转和依赖注入

    如何理解控制反转和依赖注入 文章目录 如何理解控制反转和依赖注入 前言 一.什么是控制反转 1.何为依赖关系 2.进行控制反转的前的一些准备 3.控制反转的简单实现 2.1:实现代码 二.什么是依赖注 ...

  6. 【简译】关于依赖反转原则、控制反转和依赖注入的抽象的初学者指南

    原文在此. ======================================分割线==================================== 介绍 文章以介绍依赖反转原则开始 ...

  7. 依赖倒置原则(DIP)、控制反转(IoC)、依赖注入(DI)(C#)

    象的控制权交由配置文件控制,然后根据配置文件中的信息(程序集+类型),通过反射来获取对象,而不是直接new对象,这也是控制反转的一种体现. IoC容器会连接程序中的所有模块,模块将所需对象的控制权都交 ...

  8. java-12:spring MVC - 控制反转IOC,依赖注入DI

    学习spring框架之前,先理解几个概念: 1.第一部分:依赖倒置原则 2.第二部分:控制反转,控制反转容器(实例) 3.第三部分:控制反转,控制反转容器(全面理解,面试题) 综合性理解:控制反转(I ...

  9. 控制反转( IoC)和依赖注入(DI)

    控制反转( IoC)和依赖注入(DI) tags: 容器 依赖注入 IOC DI 控制反转 引言:如果你看过一些框架的源码或者手册,像是laravel或者tp5之类的,应该会提到容器,依赖注入,控制反 ...

  10. Java:控制反转(IoC)与依赖注入(DI)

    很长一段时间里,我对控制反转和依赖注入这两个概念很模糊,闭上眼睛想一想,总有一种眩晕的感觉.但为了成为一名优秀的 Java 工程师,我花了一周的时间,彻底把它们搞清楚了. 01.紧耦合 在我们编码的过 ...

最新文章

  1. linux内核配置失败,petalinux配置内核出现下面错误
  2. Eclipse导入项目常见问题----服务器版本问题02
  3. ABAP程序系统字段中英文详解
  4. selenium,webdriver 执行js语句 对象是百度
  5. 9.关于Unicode字符集
  6. 关于RestTemplate的几个问题
  7. 广义矩估计的一般步骤_【基本无害】动态理性预期理论与广义矩估计02
  8. firefox改html内容,可以firefox扩展修改HTML文档的DOM然后保存为HTML吗?
  9. 千年3 『自动杀猪·无限挂』千年3脚本 千年3外挂
  10. 一文读懂hosts文件
  11. Sicily2000——Toy Shopping
  12. Vue elementUI中的Breadcrumb面包屑
  13. JS两个日期之间计算时间差
  14. react antd Table 表格 td超出自动换行
  15. 2. Vmware vCenter部署
  16. 『Excel』常用五大类函数汇总
  17. Vue 前端导出Excel表格,多级表头合并
  18. 输入年、月,判断该月有多少天
  19. ROS古月资料学习之ROS简介
  20. 【项目】实现一个mini的tcmalloc(高并发内存池)

热门文章

  1. 动态添加上传控件,并上传文件
  2. C#中Dictionary的用法及用途
  3. QT4升级QT5调研报告
  4. 行军导航过程中导向箭头
  5. 消防信号二总线有没电压_杭后旗医院消防消防设备电源原理
  6. mysql查询忽略字符编码是什么_MySQL 查询不区分大小写的问题以及编码格式问题...
  7. python用sort()函数对列表进行排序,从最后一个元素开始判断,超详细讲解,图文+视频
  8. 内蒙古农业大学微型计算机,内蒙古农业大学微机原理重点
  9. 蛋糕是叫胚子还是坯子_这个生日蛋糕太适合手残党了,不会裱花也能做,学会再不买着吃了...
  10. C#利用反射,遍历获得一个类的所有属性名,以及该类的实例的所有属性的值