zoukankan      html  css  js  c++  java
  • 动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化

    动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化

    Intro

    之前实现的那版依赖注入框架基本可用,但是感觉还是不够灵活,而且注册服务和解析服务在同一个地方感觉有点别扭,有点职责分离不够。于是借鉴 Autofac 的做法,增加了一个 ServiceContainerBuilder 来负责注册服务,ServiceContainer负责解析服务,并且增加了一个 ServiceContainerModule 可以支持像 Autofac 中 Module/RegisterAssemblyModules 一样注册服务

    实现代码

    ServiceContainerBuilder

    增加 ServiceContainerBuild 来专门负责注册服务,原来注册服务的那些扩展方法则从 IServiceContainer 的扩展方法变成 IServiceContainerBuilder 的扩展

    public interface IServiceContainerBuilder
    {
        IServiceContainerBuilder Add(ServiceDefinition item);
    
        IServiceContainerBuilder TryAdd(ServiceDefinition item);
    
        IServiceContainer Build();
    }
    
    public class ServiceContainerBuilder : IServiceContainerBuilder
    {
        private readonly List<ServiceDefinition> _services = new List<ServiceDefinition>();
    
        public IServiceContainerBuilder Add(ServiceDefinition item)
        {
            if (_services.Any(_ => _.ServiceType == item.ServiceType && _.GetImplementType() == item.GetImplementType()))
            {
                return this;
            }
    
            _services.Add(item);
            return this;
        }
    
        public IServiceContainerBuilder TryAdd(ServiceDefinition item)
        {
            if (_services.Any(_ => _.ServiceType == item.ServiceType))
            {
                return this;
            }
            _services.Add(item);
            return this;
        }
    
        public IServiceContainer Build() => new ServiceContainer(_services);
    }
    

    IServiceContainer

    增加 ServiceContainerBuilder 之后就不再支持注册服务了,ServiceContainer 这个类型也可以变成一个内部类了,不必再对外暴露

    public interface IServiceContainer : IScope, IServiceProvider
    {
        IServiceContainer CreateScope();
    }
    
    internal class ServiceContainer : IServiceContainer
    {
        private readonly IReadOnlyList<ServiceDefinition> _services;
        
        public ServiceContainer(IReadOnlyList<ServiceDefinition> serviceDefinitions)
        {
            _services = serviceDefinitions;
            // ...
        }
        
        // 此处约省略一万行代码 ...
    }
    

    ServiceContainerModule

    定义了一个 ServiceContainerModule 来实现像 Autofac 那样,在某一个程序集内定义一个 Module 注册程序集内需要注册的服务,在服务注册的地方调用 RegisterAssemblyModules 来扫描所有程序集并注册自定义 ServiceContainerModule 需要注册的服务

    public interface IServiceContainerModule
    {
        void ConfigureServices(IServiceContainerBuilder serviceContainerBuilder);
    }
    
    public abstract class ServiceContainerModule : IServiceContainerModule
    {
        public abstract void ConfigureServices(IServiceContainerBuilder serviceContainerBuilder);
    }
    

    自定义 ServiceContainerModule 使用示例:

    public class TestServiceContainerModule : ServiceContainerModule
    {
        public override void ConfigureServices(IServiceContainerBuilder serviceContainerBuilder)
        {
            serviceContainerBuilder.AddSingleton<IIdGenerator>(GuidIdGenerator.Instance);
        }
    }
    

    RegisterAssemblyModules 扩展方法实现如下:

    
    public static IServiceContainerBuilder RegisterAssemblyModules(
        [NotNull] this IServiceContainerBuilder serviceContainerBuilder, params Assembly[] assemblies)
    {
        #if NET45
            // 解决 asp.net 在 IIS 下应用程序域被回收的问题
            // https://autofac.readthedocs.io/en/latest/register/scanning.html#iis-hosted-web-applications
            if (null == assemblies || assemblies.Length == 0)
            {
                if (System.Web.Hosting.HostingEnvironment.IsHosted)
                {
                    assemblies = System.Web.Compilation.BuildManager.GetReferencedAssemblies()
                        .Cast<Assembly>().ToArray();
                }
            }
        #endif
    
            if (null == assemblies || assemblies.Length == 0)
            {
                assemblies = AppDomain.CurrentDomain.GetAssemblies();
            }
    
        foreach (var type in assemblies.WhereNotNull().SelectMany(ass => ass.GetTypes())
                 .Where(t => t.IsClass && !t.IsAbstract && typeof(IServiceContainerModule).IsAssignableFrom(t))
                )
        {
            try
            {
                if (Activator.CreateInstance(type) is ServiceContainerModule module)
                {
                    module.ConfigureServices(serviceContainerBuilder);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
        return serviceContainerBuilder;
    }
    

    使用示例

    使用起来除了注册服务变化了之外,别的地方并没有什么不同,看一下单元测试代码

    public class DependencyInjectionTest : IDisposable
    {
        private readonly IServiceContainer _container;
    
        public DependencyInjectionTest()
        {
            var containerBuilder = new ServiceContainerBuilder();
            containerBuilder.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
            containerBuilder.AddScoped<IFly, MonkeyKing>();
            containerBuilder.AddScoped<IFly, Superman>();
    
            containerBuilder.AddScoped<HasDependencyTest>();
            containerBuilder.AddScoped<HasDependencyTest1>();
            containerBuilder.AddScoped<HasDependencyTest2>();
            containerBuilder.AddScoped<HasDependencyTest3>();
            containerBuilder.AddScoped(typeof(HasDependencyTest4<>));
    
            containerBuilder.AddTransient<WuKong>();
            containerBuilder.AddScoped<WuJing>(serviceProvider => new WuJing());
            containerBuilder.AddSingleton(typeof(GenericServiceTest<>));
    		
            containerBuilder.RegisterAssemblyModules();
    
            _container = containerBuilder.Build();
        }
    
        [Fact]
        public void Test()
        {
            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();
        }
    
        public void Dispose()
        {
            _container.Dispose();
        }
    }
    
    

    Reference

  • 相关阅读:
    每天进步一点点------基础实验_08_触发器 :D、T触发器各一
    每天进步一点点------基础实验_07_锁存器 :锁存器器及透明锁存器各一
    每天进步一点点------基础实验_06_优先译码器 :优先译码器
    每天进步一点点------基础实验_05_译码器 :3位输入8位输出译码器
    每天进步一点点------基础实验_04_优先编码器:8位输入3位输出高位优先
    每天进步一点点------基础实验_03_编码器 :8位输入3位输出编码器
    每天进步一点点------基础实验_02_多路解复用器 :4通道8位带三态输出
    每天进步一点点------基础实验_01_多路复用器 :4通道8位带三态输出
    每天进步一点点------时序分析基础与时钟约束实例(三)
    每天进步一点点------时序分析基础与时钟约束实例(二)
  • 原文地址:https://www.cnblogs.com/weihanli/p/implement-dependency-injection-02.html
Copyright © 2011-2022 走看看