动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化
动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化
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://github.com/WeihanLi/WeihanLi.Common/tree/dev/src/WeihanLi.Common/DependencyInjection
- 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://autofac.readthedocs.io/en/latest/register/scanning.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。