动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化
動手造輪子:實現一個簡單的依賴注入(二) --- 服務注冊優化
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
總結
以上是生活随笔為你收集整理的动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Http benchmarking 工具
- 下一篇: Magicodes.IE之导入学生数据教