• IOC:  Inversion Of Control 控制反转

  • DI:   Dependency  Injection 依赖注入

1.控制反转 Inversion Of Control 的前世今生

1.1  IOC理论产生的背景

讨论控制反转之前,先看看软件系统提出控制反转的前世今生。
一个完整精密的软件系统,组件之间就像齿轮,协同工作,相互耦合。

  • 一个零件不正常,整个系统就崩溃了。

  • 系统对象之间耦合关系无法避免,在项目规模和复杂度变大的情况下,管理类之间的依赖关系将会很复杂。

  • 对象之间耦合度很高的系统,架构师和开发人员对于系统的修改,必然会出现牵一发而动全身的情形。

  • 对象之间耦合性依赖,单元测试很复杂。

1.2 IOC理论

软件专家为此提出IOC理论,用来实现对象之间的解耦。
再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:

  1. 软件系统在没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

  2. 软件系统在引入IOC容器之后,这种情形就完全改变了,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
    通过前后对比,我们不难看出:
    对象A获得依赖对象B的过程,由主动变为了被动行为,控制权颠倒过来,这就是“控制反转”的由来。

1.3 控制反转 和 依赖注入

有些人会把控制反转和依赖注入等同,实际上有本质区别:
控制反转是一种思想;依赖注入是一种设计模式。
依赖注入是实现控制反转的一种方式,但是控制反转还有其他实现方式,例如说ServiceLocator(服务定位器、依赖查找),所以不能将控制反转和依赖注入等同。

2 依赖注入 Dependency  Injection

依赖注入:容器全权负责组件的装配,它会把符合依赖关系的对象通过属性或者构造函数传递给需要的对象。

符合依赖倒置原则,高层模块不应该依赖低层模块,两者都应该依赖其抽象

2.1 ASP.NET Core依赖注入

使用方式大体类似:
①. 定义依赖实现的接口或者抽象类
②. 在服务容器中注册组件依赖 :IServiceProvider
③. 在构造函数中注入服务, 框架会负责创建和销毁实例

// 编写组件和服务
public interface IMyDependency
{string WriteMessage(string message);
}
---
public class MyDependency : IMyDependency
{public string WriteMessage(string message){return $"MyDependency.WriteMessage Message: {message}";}
}
// 注册组件和依赖,下面注册的`IMyDependency`在一个web请求中有效
public void ConfigureServices(IServiceCollection services)
{services.AddScoped<IMyDependency, MyDependency>();services.AddRazorPages();
}
---
// 在构造函数注入组件
public class HomeController: AbpController
{private readonly IMyDependency _dep;public HomeController(IMyDependency dep){_dep = dep;}public IActionResult Index(){var content =  _dep.WriteMessage($"The Reflection instance is {_dep.GetType().FullName} ");return Content(content);}
}

在请求某个服务时,框架会完整解析出这个对象的依赖树和作用范围。

上面的示例代码形成 req--->HomeController--->IMyDependency依赖树。

IMyDependency在每个web请求范围内使用同一服务实例。

输出:MyDependency.WriteMessage Message: The Reflection instance is TestDI.MyDependency

2.2 对象生命周期

根据现实需要,前人从使用场景中总结出三种服务生命周期。
ASP.NET Core提供了一个枚举ServiceLifetime

-- --- --- ---
Singleton 单例 服务容器首次请求会创建,后续都使用同一实例 AddSingleton
Scoped 特定范围 在一个请求(连接)周期内使用一个示例 AddScoped
Transient 瞬时 服务容器每次请求,都会创建一个实例 AddTransient

对于Scoped Service的理解:在webapp:scoped service 会在请求结束时被销毁;
在EFCore:使用AddDbContext默认注册的是特定范围的DbContext,这意味在我们可以在一次sql连接内,使用同一个DbContext实例进行多次DB操作。

2.3 依赖注入实现原理

结合理论、使用方式 猜测依赖注入的原理:
实现DI,核心在于依赖注入容器IContainer,该容器具有以下功能
①.(容器)保存可用服务的集合
//  要用的特定对象、特定类、接口服务

②.(注册)提供一种方式将各种部件与他们依赖的服务绑定到一起;
//  Add...函数或containerBuilder.Register函数

③.(解析点)为应用程序提供一种方式来请求已配置的对象:构造函数注入、属性注入.

运行时,框架会一层层通过反射构造实例,最终得到完整对象。

3.源码导航

利用反射产生对象是依赖注入的核心过程,这也是面试造航母时经常问到的。

.NETSystem.ReflectionSystem.Type命名空间中的类可以获取可装配组件、类、接口的信息,并提供了在运行时创建实例,调用动态实例方法、获取动态实例的能力。

当我尝试从github源码中探究[依赖注入产生对象]的伪代码时,文件/代码众多,迷路了!

实际上,我们可以在依赖树的尾部对象的构造函数手动抛出异常,异常的调用栈就是一个天然的源码导航。

于是我在上面示例代码的request----> HomeController--->MyDependency MyDependency构造函数中添加异常代码:

    public MyDependency(){throw new Exception("exception content!");}

结果如下图:从Github Dependency Injection 库进入System.Reflection的调用分界线代码:

protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{object[] parameterValues;if (constructorCallSite.ParameterCallSites.Length == 0){parameterValues = Array.Empty<object>();}else{parameterValues = new object[constructorCallSite.ParameterCallSites.Length];for (var index = 0; index < parameterValues.Length; index++){parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);}}try{return constructorCallSite.ConstructorInfo.Invoke(parameterValues);}catch (Exception ex) when (ex.InnerException != null){ExceptionDispatchInfo.Capture(ex.InnerException).Throw();// The above line will always throw, but the compiler requires we throw explicitly.throw;}
}

黄色背景行就是.NET反射特性的体现:
对类型信息(构造函数、参数)使用Invoke方法产生对象。

干货旁白

  1. 控制反转是一种在软件工程中解耦合的思想,调用方依赖接口或抽象类,减少了耦合,控制权交给了服务容器,由容器维护注册项,并将具体的实现动态注入到调用方。

  2. 有些人会把控制反转和依赖注入等同,实际上有本质区别:
    控制反转是一种思想;
    依赖注入是一种设计模式。
    依赖注入是实现控制反转的一种方式,但是控制反转还有其他实现方式,例如说ServiceLocator,所以不能将控制反转和依赖注入等同。

  3. 在运行时,框架会解析依赖树、依赖图,通过反射在运行期生成对象。

学完这篇依赖注入,与面试官扯皮就没有问题了。相关推荐

  1. hashmap是有序还是无序_说实话,你要是看完这篇 HashMap ,和面试官扯皮真的就没问题了!

    文章来源:看完这篇 HashMap ,和面试官扯皮就没问题了 原文作者:cxuan 来源平台:微信公众号 (如果你没有时间细抠本文,可以直接看 HashMap 概述,能让你对 HashMap 有个大致 ...

  2. 看完这篇文章,跟面试官扯皮就没问题了

    认识 HTTP 首先你听的最多的应该就是 HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol),这你一定能说出来,但是这样还不够,假如你是大厂面试官,这不可能是他 ...

  3. 看完这篇 HTTP,跟面试官扯皮就没问题了

    作者 | cxuan 责编 | 屠敏 头图 | CSDN 下载自视觉中国 我是一名程序员,我的主要编程语言是 Java,我更是一名 Web 开发人员,所以我必须要了解 HTTP,所以本篇文章就来带你从 ...

  4. 请输入要搜索的内容 搜索 13 转发 微博 Qzone 微信 看完这篇HTTP,跟面试官扯皮就没问题了

    最初在有网络之前,我们的电脑都是单机的,单机系统是孤立的,我还记得 05 年前那会儿家里有个电脑,想打电脑游戏还得两个人在一个电脑上玩儿,及其不方便.我就想为什么家里人不让上网,我的同学 xxx 家里 ...

  5. 看完这篇操作系统,和面试官扯皮就没问题了。

    点击蓝色"程序员cxuan "关注我哟 加个"星标",欢迎来撩 这是程序员cxuan的第七篇原创文章 1 解释一下什么是操作系统 操作系统是运行在计算机上最重要 ...

  6. 看完这篇 HTTPS,和面试官扯皮就没问题了

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:为什么魂斗罗只有 128 KB却可以实现那么长的剧情?个人原创+1博客:点击前往,查看更多 下面我们来一起学习一 ...

  7. 看完这篇 HashMap ,和面试官扯皮就没问题了

    来源 | Java 建设者 责编 | Carol 封图 | CSDN 下载自视觉中国 (如果你没有时间细抠本文,可以直接看 HashMap 概述,能让你对 HashMap 有个大致的了解) HashM ...

  8. excel正在等待某个应用程序以完成对象链接与嵌入操作_看完这篇操作系统,和面试官扯皮就没问题了

    各位看官内容喜欢的话,动动手指点个 ,点个关注呗!!谢谢支持! 解释一下什么是操作系统 操作系统是运行在计算机上最重要的一种软件,它管理计算机的资源和进程以及所有的硬件和软件.它为计算机硬件和软件提供 ...

  9. 看完这篇操作系统,和面试官扯皮就没问题了!

    作者 | Cxuan 责编 | Carol 来源 | 程序员 cxuan 封图 | CSDN 付费下载于视觉中国 1.解释一下什么是操作系统 操作系统是运行在计算机上最重要的一种软件,它管理计算机的资 ...

最新文章

  1. swift使用xib绘制UIView
  2. python适合零基础学习吗-零基础能学好Python吗?哪些人更适合学习?
  3. 2019秋第三周学习总结
  4. mahout基于Hadoop的CF代码分析(转)
  5. java io 结构_java 的IO类库的基本架构
  6. 为什么对开发者很重要?
  7. php中如何将验证码放入页面,如何在php中生成验证码图片
  8. 数据资产标准研究进展与建议
  9. poj-1201 Intervals(差分约束)
  10. Java事务管理之JDBC
  11. Spring Cloud实战(六)-Spring Cloud Netflix Bus
  12. 微信UnionID的作用
  13. PS透明婚纱抠图(超详细)
  14. 解决Idea 出现 Could not autowire.. 错误
  15. 【转载】日本社会为啥没有“王思聪”
  16. 商品详情页中间显示不出来
  17. Qt设计的一个图片查看器
  18. 炼数成金CUDA视频教程——第三课1——学习笔记
  19. 微信开挂怎么防止封号_再也不怕被封号!微信养号秘笈教你防封号防降权
  20. 让QQ的文字漂亮起来

热门文章

  1. omnipay支付--支付宝支付
  2. python实现批量压缩文件夹
  3. 【Python】HackBack(获取暴力破解服务器密码的IP来源)
  4. ExtJs5.0在WebStorm上的使用之入门教程(一)编写第一个网页 HelloExt
  5. jQuery Pagination Ajax分页插件中文详解
  6. Mac OS使用技巧之十:Finder的详细使用方法
  7. android页面布局 如何让中间的listview填充剩余部分_谷歌驾驶设计—界面设计布局...
  8. ES6入门之Symbol
  9. BZOJ4596:[SHOI2016]黑暗前的幻想乡——题解
  10. oncontextmenu事件