本文共 6309 字,大约阅读时间需要 21 分钟。
之前实现的那版依赖注入框架基本可用,但是感觉还是不够灵活,而且注册服务和解析服务在同一个地方感觉有点别扭,有点职责分离不够。于是借鉴 Autofac 的做法,增加了一个 ServiceContainerBuilder
来负责注册服务,ServiceContainer
负责解析服务,并且增加了一个 ServiceContainerModule
可以支持像 Autofac 中 Module
/RegisterAssemblyModules
一样注册服务
增加 ServiceContainerBuild
来专门负责注册服务,原来注册服务的那些扩展方法则从 IServiceContainer
的扩展方法变成 IServiceContainerBuilder
的扩展
public interface IServiceContainerBuilder{ IServiceContainerBuilder Add(ServiceDefinition item); IServiceContainerBuilder TryAdd(ServiceDefinition item); IServiceContainer Build();}public class ServiceContainerBuilder : IServiceContainerBuilder{ private readonly List_services = new List (); 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);}
增加 ServiceContainerBuilder
之后就不再支持注册服务了,ServiceContainer
这个类型也可以变成一个内部类了,不必再对外暴露
public interface IServiceContainer : IScope, IServiceProvider{ IServiceContainer CreateScope();}internal class ServiceContainer : IServiceContainer{ private readonly IReadOnlyList_services; public ServiceContainer(IReadOnlyList serviceDefinitions) { _services = serviceDefinitions; // ... } // 此处约省略一万行代码 ...}
定义了一个 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(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().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(new ConfigurationBuilder().Build()); containerBuilder.AddScoped (); containerBuilder.AddScoped (); containerBuilder.AddScoped (); containerBuilder.AddScoped (); containerBuilder.AddScoped (); containerBuilder.AddScoped (); containerBuilder.AddScoped(typeof(HasDependencyTest4<>)); containerBuilder.AddTransient (); containerBuilder.AddScoped (serviceProvider => new WuJing()); containerBuilder.AddSingleton(typeof(GenericServiceTest<>)); containerBuilder.RegisterAssemblyModules(); _container = containerBuilder.Build(); } [Fact] public void Test() { var rootConfig = _container.ResolveService (); Assert.Throws (() => _container.ResolveService ()); Assert.Throws (() => _container.ResolveRequiredService ()); using (var scope = _container.CreateScope()) { var config = scope.ResolveService (); Assert.Equal(rootConfig, config); var fly1 = scope.ResolveRequiredService (); var fly2 = scope.ResolveRequiredService (); Assert.Equal(fly1, fly2); var wukong1 = scope.ResolveRequiredService (); var wukong2 = scope.ResolveRequiredService (); Assert.NotEqual(wukong1, wukong2); var wuJing1 = scope.ResolveRequiredService (); var wuJing2 = scope.ResolveRequiredService (); Assert.Equal(wuJing1, wuJing2); var s0 = scope.ResolveRequiredService (); s0.Test(); Assert.Equal(s0._fly, fly1); var s1 = scope.ResolveRequiredService (); s1.Test(); var s2 = scope.ResolveRequiredService (); s2.Test(); var s3 = scope.ResolveRequiredService (); s3.Test(); var s4 = scope.ResolveRequiredService >(); s4.Test(); using (var innerScope = scope.CreateScope()) { var config2 = innerScope.ResolveRequiredService (); Assert.True(rootConfig == config2); var fly3 = innerScope.ResolveRequiredService (); fly3.Fly(); Assert.NotEqual(fly1, fly3); } var flySvcs = scope.ResolveServices (); foreach (var f in flySvcs) f.Fly(); } var genericService1 = _container.ResolveRequiredService >(); genericService1.Test(); var genericService2 = _container.ResolveRequiredService >(); genericService2.Test(); } public void Dispose() { _container.Dispose(); }}
转载地址:http://uakki.baihongyu.com/