动手造轮子:实现一个简单的依赖注入(零)

动手造轮子:实现一个简单的依赖注入(零)

Intro

依赖注入为我们写程序带来了诸多好处,在微软的 .net core 出来的同时也发布了微软开发的依赖注入框架 Microsoft.Extensions.DependencyInjection,大改传统 asp.net 的开发模式,asp.net core 的开发更加现代化,更加灵活,更加优美。

依赖注入介绍

要介绍依赖注入,首先来聊一下控制反转(IoC)

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

  • 谁控制谁,控制什么:传统程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由 IoC 容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

IoC 对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在 IoC/DI 思想中,应用程序就变成被动的了,被动的等待 IoC 容器来创建并注入它所需要的资源了。

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

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

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

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

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

  ●谁注入谁:很明显是 IoC 容器注入应用程序里依赖的对象

  ●注入了什么:就是注入某个对象所需要的外部资源/依赖

 

依赖注入明确描述了 “被注入对象依赖 IoC 容器配置依赖对象”,依赖注入是控制反转设计思想的一种实现。

依赖注入的好处:

  • 对象的创建和销毁完全交给 ioc 容器去做,不再需要在应用中关心对象的创建的和销毁,这对于 C# 里的 IDisposable 对象来说尤为重要,自己去 new 的时候,对于一些新手来说可能会忘记使用 using 或手动 dispose

  • 对象的复用,有时候很多对象没有必要每次用的时候就去创建一次,使用 ioc 可以控制在同一生命周期内的对象只被创建一次

  • 依赖关系更清晰

  • 更好的实现面向接口编程,替换实现只需要注入服务的时候换成另外一种实现就可以了

大概设计

大体使用类似于微软的依赖注入框架,但是比微软的依赖注入框架简单一些,性能也有待优化。

  • 服务生命周期:服务的生命周期沿用微软的服务生命周期,分为 SingletonScopedTransient,默认值是 Singleton 单例模式

  • 服务注册方式:支持所有微软依赖注入的注册方式,实例注入/类型注入/接口-实现注入/func 注入

  • 注入方式:目前仅支持依赖注入,构造方法注入,未来暂时也没有支持属性注入的打算(支持的话也不复杂,但是依赖关系就不清晰了,也不推荐用),构造方法注入支持直接注入 IEnumerable<T> 或 IReadOnlyCollection<T> 或 IReadOnlyList<T> 来支持获取一个接口多个实现的注入,支持泛型注入

DI 相关类图:

640?wx_fmt=png

体验一下

可以参考单元测试:

using(IServiceConatiner container = new ServiceContainer())	
{	container.AddSingleton<IConfiguration>(new ConfigurationBuilder()	.AddJsonFile("appsettings.json")	.Build()	);	container.AddScoped<IFly, MonkeyKing>();	container.AddScoped<IFly, Superman>();	container.AddScoped<HasDependencyTest>();	container.AddScoped<HasDependencyTest1>();	container.AddScoped<HasDependencyTest2>();	container.AddScoped<HasDependencyTest3>();	container.AddScoped(typeof(HasDependencyTest4<>));	container.AddTransient<WuKong>();	container.AddScoped<WuJing>(serviceProvider => new WuJing());	container.AddSingleton(typeof(GenericServiceTest<>));	var rootConfig = container.ResolveService<IConfiguration>();	Assert.Throws<InvalidOperationException>(() => container.ResolveService<IFly>());	Assert.Throws<InvalidOperationException>(() => container.ResolveRequiredService<IDependencyResolver>());	using (var scope = container.CreateScope())	{	var config = scope.ResolveService<IConfiguration>();	Assert.Equal(rootConfig, config);	var fly1 = scope.ResolveRequiredService<IFly>();	var fly2 = scope.ResolveRequiredService<IFly>();	Assert.Equal(fly1, fly2);	var wukong1 = scope.ResolveRequiredService<WuKong>();	var wukong2 = scope.ResolveRequiredService<WuKong>();	Assert.NotEqual(wukong1, wukong2);	var wuJing1 = scope.ResolveRequiredService<WuJing>();	var wuJing2 = scope.ResolveRequiredService<WuJing>();	Assert.Equal(wuJing1, wuJing2);	var s0 = scope.ResolveRequiredService<HasDependencyTest>();	s0.Test();	Assert.Equal(s0._fly, fly1);	var s1 = scope.ResolveRequiredService<HasDependencyTest1>();	s1.Test();	var s2 = scope.ResolveRequiredService<HasDependencyTest2>();	s2.Test();	var s3 = scope.ResolveRequiredService<HasDependencyTest3>();	s3.Test();	var s4 = scope.ResolveRequiredService<HasDependencyTest4<string>>();	s4.Test();	using (var innerScope = scope.CreateScope())	{	var config2 = innerScope.ResolveRequiredService<IConfiguration>();	Assert.True(rootConfig == config2);	var fly3 = innerScope.ResolveRequiredService<IFly>();	fly3.Fly();	Assert.NotEqual(fly1, fly3);	}	var flySvcs = scope.ResolveServices<IFly>();	foreach (var f in flySvcs)	f.Fly();	}	var genericService1 = container.ResolveRequiredService<GenericServiceTest<int>>();	genericService1.Test();	var genericService2 = container.ResolveRequiredService<GenericServiceTest<string>>();	genericService2.Test();	
}

更多详情可以参考:< https://github.com/WeihanLi/WeihanLi.Common/blob/dev/test/WeihanLi.Common.Test/DependencyInjectionTest.cs >

More

源码已经在 Github 上,可以自行下载阅览或等后面的几篇文章分享解读

Reference

  • https://blog.csdn.net/sinat_21843047/article/details/80297951

  • https://www.cnblogs.com/artech/p/inside-asp-net-core-03-04.html

  • https://github.com/aspnet/DependencyInjection/tree/rel/2.0.0

  • https://github.com/microsoft/MinIoC

  • https://github.com/WeihanLi/WeihanLi.Common/tree/dev/src/WeihanLi.Common/DependencyInjection

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

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

相关文章

【WPF on .NET Core 3.0】 Stylet演示项目 - 简易图书管理系统(2)

上一章《回忆一下我们的登录逻辑,主要有以下4点:当"用户名"或"密码"为空时, 是不允许登录的("登录"按钮处于禁用状态).用户名或密码不正确时, 显示"用户名或密码不正确"的消息框.用户名输入"waku", 并且密码输入"123&q…

gRPC 流式调用

gRPC 使用 Protocol buffers 作为接口定义语言&#xff08;IDL&#xff09;来描述服务接口和输入输出消息的结构&#xff0c;目前支持 4 种定义服务方法类型&#xff1a;类型说明简单 RPC客户端传入一个请求对象&#xff0c;服务端返回一个结果对象客户端流式 RPC客户端传入多个…

模型压缩案例-SSDYou only look once

http://write.blog.csdn.NET/postedit 在上一篇文章中&#xff0c;介绍了以regionproposal来检测的框架&#xff0c;这一系列速度和精度不断提高&#xff0c;但是还是无法达到实时。存在的主要问题为&#xff1a;速度不够快&#xff0c;主要原因是proposal比较多&#xff0c;特…

.NET如何将字符串分隔为字符

前言如果这是一道面试题&#xff0c;答案也许非常简单&#xff1a;.ToCharArray()&#xff0c;这基本正确……我们以“AB吉??????”作为输入参数&#xff0c;首先如果按照“正常”处理的思路&#xff0c;用 .ToCharArray()&#xff0c;然后转换为 JSON&#xff08;以便方…

Orleans 知多少 | 3. Hello Orleans

1. 引言是的&#xff0c;Orleans v3.0.0 已经发布了&#xff0c;并已经完全支持 .NET Core 3.0。所以&#xff0c;Orleans 系列是时候继续了&#xff0c;抱歉&#xff0c;让大家久等了。万丈高楼平地起&#xff0c;这一节我们就先来了解下Orleans的基本使用。2. 模板项目讲解在…

.NET Core 3.0之深入源码理解ObjectPool(二)

写在前面前文主要介绍了ObjectPool的一些理论基础&#xff0c;本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的。下图为其三大核心组件图&#xff1a;核心组件ObjectPoolObjectPool是一个泛型抽象类&#xff0c;里面只有两个抽象方法&#xff0c;Get和Retu…

VC维学习

http://www.flickering.cn/machine_learning/2015/04/vc维的来龙去脉/ 说说历史Hoeffding不等式Connection to Learning学习可行的两个核心条件Effective Number of HypothesesGrowth FunctionBreak Point与ShatterVC BoundVC dimension深度学习与VC维小结参考文献 VC维在机器学…

.NET Core 3.0 一个 jwt 的轻量角色/用户、单个API控制的授权认证库

作者&#xff1a;痴者工良&#xff08;朋友合作原创&#xff09;来源&#xff1a;https://www.cnblogs.com/whuanle/p/11743406.html目录说明一、定义角色、API、用户二、添加自定义事件三、注入授权服务和中间件三、如何设置API的授权四、添加登录颁发 Token五、部分说明六、验…

.NET Core 3.0 构建和部署

Default Executables 默认可执行文件 在 dotnet build 或 dotnet publish 期间&#xff0c;将创建一个与你使用的 SDK 的环境和平台相匹配的可执行文件。 和其他本机可执行文件一样&#xff0c;可以使用这些可执行文件执行相同操作&#xff0c;例如&#xff1a; 可以双击可执行…

为什么我会了SOA,你们还要逼我学微服务?

菜菜哥&#xff0c;我最近需要做一个项目&#xff0c;老大让我用微服务的方式来做那挺好呀&#xff0c;微服务现在的确很流行我以前在别的公司都是以SOA的方式&#xff0c;SOA也是面向服务的方式呀的确&#xff0c;微服务和SOA有相同之处面向服务的架构&#xff08;SOA&#xf…

面对万物互联的智能世界,你是否也想分一杯羹

第六届世界互联网大会于10月20日至22日在浙江乌镇顺利举行。作为世界互联网大会“13”架构的重要组成部分&#xff0c;“互联网之光”博览会以“智能互联网、开放合作——携手共建网络空间命运共同体”为主题&#xff0c;集中展示了全球范围内的互联网新技术、新成果、新产品、…

你必须知道的容器监控 (2) cAdvisor

# 实验环境&#xff1a;阿里云ECS主机&#xff08;两台&#xff09;&#xff0c;CentOS 7.401—cAdvisor简介为了解决容器的监控问题&#xff0c;Google开发了一款容器监控工具cAdvisor&#xff08;Container Advisor&#xff09;&#xff0c;它为容器用户提供了对其运行容器的…

代码阅读

http://alanse7en.github.io/caffedai-ma-jie-xi-4/ 三. 从一个比较宏观的层面上去了解caffe怎么去完成一些初始化的工作和使用Solver的接口函数&#xff0c;本文将主要分为四部分的内容&#xff1a; Google Flags的使用Register Brew Function的宏的定义和使用train()函数的…

动手造轮子:实现一个简单的依赖注入(一)

动手造轮子&#xff1a;实现一个简单的依赖注入(一)Intro在上一篇文章中主要介绍了一下要做的依赖注入的整体设计和大概编程体验&#xff0c;这篇文章要开始写代码了&#xff0c;开始实现自己的依赖注入框架。类图首先来温习一下上次提到的类图服务生命周期服务生命周期定义&am…

Qt 调试Caffe

http://blog.csdn.net/xg123321123/article/details/52817658 1.下载并安装Qt Creator 下载页面&#xff0c;推荐使用4.x版本&#xff0c;比如&#xff1a; Qt Creator 4.1.0 for Linux 64-bit下载的是run包&#xff0c;安装方法&#xff1a; cd到下载目录 chmod x xxx.run …

.NET Core 3.0 本地工具

.NET Core从最早期的版本就开始支持全局工具了。如果仅仅需要在某个项目中或某个文件夹中使用特定的工具&#xff0c;那么.NET Core 3.0就允许您这样做。 使用.NET Core 3.0&#xff0c;您可以在特定的文件夹下安装“本地”工具&#xff0c;它的作用范围仅限于该文件夹及其子文…

Nsight 调试 Caffe

http://blog.csdn.net/u014114990/article/details/47723877 右键属性菜单的General->Code Analysis->Paths and Symbols下进行加入&#xff1a; Includes下加入程序需要用到的头文件的路径&#xff1a; Library Path下添加需要用到库文件的路径&#xff1a; 具体用到的…

张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出

什么是 PWM在解释 PWM 之前首先来了解一下电路中信号的概念&#xff0c;其中包括模拟信号和数字信号。模拟信号是一种连续的信号&#xff0c;与连续函数类似&#xff0c;在图形上表现为一条不间断的连续曲线。数字信号为只能取有限个数值的信号&#xff0c;比如计算机中的高电平…

.NET Core 3 对 IoT 应用程序的高级支持:System.Device.Gpio

System.Device.Gpio 是一个全新的 .Net Core 开源库&#xff0c;它旨在使 IoT&#xff08;物联网&#xff09;应用程序能够通过其 GPIO 引脚或其他 I/O 控制硬件与传感器、显示器和输入设备进行交互。该库是由社区维护的多个设备绑定集合来进行增强实现的。正如微软 .NET 项目…