百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

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

bigegpt 2024-08-15 20:00 3 浏览

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 可以控制在同一生命周期内的对象只被创建一次
  • 依赖关系更清晰
  • 更好的实现面向接口编程,替换实现只需要注入服务的时候换成另外一种实现就可以了

大概设计

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

  • 服务生命周期:服务的生命周期沿用微软的服务生命周期,分为 Singleton/Scoped/Transient,默认值是 Singleton 单例模式
  • 服务注册方式:支持所有微软依赖注入的注册方式,实例注入/类型注入/接口-实现注入/func 注入
  • 注入方式:目前仅支持依赖注入,构造方法注入,未来暂时也没有支持属性注入的打算(支持的话也不复杂,但是依赖关系就不清晰了,也不推荐用),构造方法注入支持直接注入 IEnumerable<T> 或 IReadOnlyCollection<T> 或 IReadOnlyList<T> 来支持获取一个接口多个实现的注入,支持泛型注入

DI 相关类图:

image

体验一下

可以参考单元测试:

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://www.jianshu.com/p/4924f22ada71

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关推荐

了解Linux目录,那你就了解了一半的Linux系统

大到公司或者社群再小到个人要利用Linux来开发产品的人实在是多如牛毛,每个人都用自己的标准来配置文件或者设置目录,那么未来的Linux则就是一团乱麻,也对管理造成许多麻烦。后来,就有所谓的FHS(F...

Linux命令,这些操作要注意!(linux命令?)

刚玩Linux的人总觉得自己在演黑客电影,直到手滑输错命令把公司服务器删库,这才发现命令行根本不是随便乱用的,而是“生死簿”。今天直接上干货,告诉你哪些命令用好了封神!喜欢的一键三连,谢谢观众老爷!!...

Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!

在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...

linux必学的60个命令(linux必学的20个命令)

以下是Linux必学的20个基础命令:1.cd:切换目录2.ls:列出文件和目录3.mkdir:创建目录4.rm:删除文件或目录5.cp:复制文件或目录6.mv:移动/重命名文件或目录7....

提高工作效率的--Linux常用命令,能够决解95%以上的问题

点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf为什么要学习Linux命令?1、因为Li...

15 个实用 Linux 命令(linux命令用法及举例)

Linux命令行是系统管理员、开发者和技术爱好者的强大工具。掌握实用命令不仅能提高效率,还能解锁Linux系统的无限潜力,本文将深入介绍15个实用Linux命令。ls-列出目录内容l...

Linux 常用命令集合(linux常用命令全集)

系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMBIOS/DM...

Linux的常用命令就是记不住,怎么办?

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux常用文件操作命令(linux常用文件操作命令有哪些)

ls命令在Linux维护工作中,经常使用ls这个命令,这是最基本的命令,来写几条常用的ls命令。先来查看一下使用的ls版本#ls--versionls(GNUcoreutils)8.4...

Linux 常用命令(linux常用命令)

日志排查类操作命令查看日志cat/var/log/messages、tail-fxxx.log搜索关键词grep"error"xxx.log多条件过滤`grep-E&#...

简单粗暴收藏版:Linux常用命令大汇总

号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友在Linux系统中,命令行界面(CLI)是管理员和开发人员最常用的工具之一。通过命令行,用户可...

「Linux」linux常用基本命令(linux常用基本命令和用法)

Linux中许多常用命令是必须掌握的,这里将我学linux入门时学的一些常用的基本命令分享给大家一下,希望可以帮助你们。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)1、显示日期的指令:d...

Linux的常用命令就是记不住,怎么办?于是推出了这套教程

1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...

Linux的30个常用命令汇总,运维大神必掌握技能!

以下是Linux系统中最常用的30个命令,精简版覆盖日常操作核心需求,适合快速掌握:一、文件/目录操作1.`ls`-列出目录内容`ls-l`(详细信息)|`ls-a`(显示隐藏文件)...

Linux/Unix 系统中非常常用的命令

Linux/Unix系统中非常常用的命令,它们是进行文件操作、文本处理、权限管理等任务的基础。下面是对这些命令的简要说明:**文件操作类:*****`ls`(list):**列出目录内容,显...