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
- https://www.cnblogs.com/weihanli/p/implement-dependency-injection-01.html
- https://www.cnblogs.com/weihanli/p/implement-dependency-injection.html
- https://autofac.org/
来源: https://www.cnblogs.com/weihanli/p/implement-dependency-injection-02.html