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

  • 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. 在运行时,框架会解析依赖树、依赖图,通过反射在运行期生成对象。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307300.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud微服之Nacos的学习

1:使用前提 第一步:解压启动Nocos SpringCloudAlibaba 推出了一个名为 Nacos 的注册中心&#xff0c;在国外也有大量的使用。 startup.cmd -m standalone 访问http://localhost:8848/nacos/ 第二步:服务注册 工程目录 在父工程中添加依赖 <dependency><groupI…

前端大佬谈国产开源:VUE 的成功在于社区运营

喜欢就关注我们吧&#xff01;近日&#xff0c;国内首个开源软件基金会 —— 开放原子基金会在深圳国际开源谷举办了 TOC 圆桌论坛。论坛上&#xff0c;基金会 TOC 之一、国内知名前端大佬贺师俊表达了一些有趣的观点&#xff0c;阐述了自己对于国内开源生态的见解。贺师俊认为…

停止精神内耗 每日分享

停止反复犹豫 作家脱不花曾说&#xff1a;“人总是有很多左右为难的事情&#xff0c;如果你在做与不做之间来回纠结&#xff0c;那就停止来回推演&#xff0c;立即去做” 所谓三思而后行 那么如果只停留在三思 那么所有的愿景都将成为遗憾 放弃脑海当中左右不定的想法 扔掉徘…

eShopOnContainers 知多少[12]:Envoy gateways

1. 引言在最新的eShopOnContainers 3.0 中Ocelot 网关被Envoy Proxy 替换。下面就来简要带大家了解下Envoy&#xff0c;并尝试梳理下为什么要使用Envoy替代Ocelot。2. Hello EnvoyENVOY IS AN OPEN SOURCE EDGE AND SERVICE PROXY, DESIGNED FOR CLOUD-NATIVE APPLICATIONS.En…

为啥 Response.Write 后,View就不渲染了?

一&#xff1a;背景 1. 讲故事前几天群里有一位朋友聊到&#xff0c;为什么我在 Action 中执行一句 Response.Write 之后&#xff0c;后续的 View 就不呈现了&#xff0c;如果脑子中没有画面&#xff0c;那就上测试代码&#xff1a;public class HomeController : Controller{p…

3. 无重复字符的最长子串(滑动窗口详解版)

一:题目 二&#xff1a;上码 class Solution {/**分析&#xff1a;1.滑动窗口问题2.何时更改窗口的起点位置当出现重复的时候 需要从重复的字符的下一个字符开始计算*/public int lengthOfLongestSubstring(String s) {if (s.length() < 1) {return s.length();}int slowIn…

121. 买卖股票的最佳时机

一:题目 二:上码 class Solution {// public int maxProfit(int[] prices) {// int max 0;// for (int i 0; i < prices.length; i) {// //求出i后面的最大值// int temp 0;// for (int j i 1; j < prices.length; j) {// …

IdentityServer4系列 | 快速搭建简易项目

一 、前言从上一篇关于 常见术语说明中&#xff0c;主要是对「IdentityServer4」的说明&#xff0c;以及其中涉及常见的术语的表述说明&#xff0c;包括对身份认证服务器、用户、客户端、资源以及各个令牌等进行对比区别说明。而在这一篇中&#xff0c;我们将尝试通过简单的方式…

136. 只出现一次的数字(hot100)

一:题目 二:上码 class Solution {public int singleNumber(int[] nums) {int ans -100;Arrays.sort(nums);for (int i 0; i < nums.length-1; i2) {if (nums[i] ! nums [i1]) {ans nums[i];break;}}return ans -100 ? nums[nums.length-1] : ans;} }

高并发项目Java是标配?.NET Core要将它拉下“神坛”!

电商的秒杀和抢购&#xff0c;对我们来说&#xff0c;都不是一个陌生的东西。然而&#xff0c;从技术的角度来说&#xff0c;这对于Web系统是一个巨大的考验。当一个Web系统&#xff0c;在一秒钟内收到数以万计甚至更多请求时&#xff0c;系统的优化和稳定至关重要。缓存技术是…

141. 环形链表(hot100)

一:题目 二:上码 /*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {public boolean hasCycle(ListNode head) {ListNode fa…

程序员修神之路--分布式系统使用网关到底是好还是坏?

“灵魂拷问分布式系统需要统一的网关吗&#xff1f;网关会带来哪些优势&#xff1f;引入网关会带来灾难吗&#xff1f;分布式系统的设计大体上分为中心化和非中心化&#xff0c;像现在流行的微服务模式&#xff0c;本质上是把各种业务拆分为独立的进程来实现业务的扩展性。伴随…

160. 相交链表(hot100)

一:题目 二:上码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListN…

.NET Core开源任务调度平台ScheduleMaster上新了

ScheduleMaster上一次比较大的更新还是在6月份&#xff0c;转眼已经快过去4个月了&#xff0c;这段时间比较忙&#xff0c;中间只更新过一次修复了几个小bug。要总结这次更新的话&#xff0c;必须要用“千呼万唤始出来”了&#xff0c;因为这次不仅经历的时间比较久&#xff0c…

234. 回文链表(hot100)

一:题目 二:上码 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ …

T-SQL | 逻辑查询处理内幕学习

【T-SQL】| 作者 / Edison Zhou这是EdisonTalk的第296篇学习分享T-SQL是ANSI和ISO SQL标准的MS SQL扩展&#xff0c;其正式名称为Transact-SQL&#xff0c;但一般程序员都称其为T-SQL。本文是我学习《T-SQL查询》一书的读书笔记&#xff0c;为你讲解逻辑查询的内幕。1逻辑查询处…

461. 汉明距离(详解)

一:题目 二:上码 class Solution {/**在信息论中&#xff0c;两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数换句话说就是将字符串转换成另一个字符串需要替换的字符的个数*/public int hammingDistance(int x, int y) {int count 0;if (x y) return 0;…

在 Azure 上给我的博客配置负载均衡

点击上方蓝字关注“汪宇杰博客”导语前阵有美国读者嘲讽我的博客在美国地区页面加载速度太慢&#xff0c;还好意思写性能优化的文章。为了让美国朋友们闭嘴&#xff0c;并不给中国人丢脸&#xff0c;我使用了钞能力&#xff0c;在 Azure 国际版上给我的博客部署了一个美国地区的…

543. 二叉树的直径(详解)

一:题目 二:上码 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

C#实现乞丐版IOC容器

一、前言netcore中的容器非常好用&#xff0c;今天我们自己来简单实现一个。实现容器的核心接口有两个&#xff1a;IServiceCollection、IServiceProvider。其中IServiceCollection定义容器集合&#xff0c;IServiceProvider提供容器中对象的访问。话不多说&#xff0c;直接编码…