ASP.NET Core见识录Amazing .NETAmazing Arch

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

2019-10-27  本文已影响0人  天天向上卡索

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

Intro

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

依赖注入介绍

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

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

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

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

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

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

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

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

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

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

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

依赖注入的好处:

大概设计

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

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://github.com/WeihanLi/WeihanLi.Common/blob/dev/test/WeihanLi.Common.Test/DependencyInjectionTest.cs >

More

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

Reference

上一篇 下一篇

猜你喜欢

热点阅读