5

重新整理 .net core 实践篇————依赖注入应用之援军[四]

 3 years ago
source link: https://www.cnblogs.com/aoximin/p/14820357.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

重新整理 .net core 实践篇————依赖注入应用之援军[四]

介绍第三方依赖注入框架Autofac,看看为我们解决什么问题。

下面介绍4个点:

  1. aop 注入

  2. 子容器命名

为什么我们需要使用第三方框架?第三方框架为我们做了什么?第三方框架扩展了哪一个部分?

这里主要介绍一下Autofac。
Autofac 主要是替换了我们ServiceProviderFactory 这个东西。

public interface IServiceProviderFactory<IContainerBuilder>

我们使用的时候这样用:

Host.CreateDefaultBuilder(args).UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
	webBuilder.UseStartup<Startup>();
});

看下UseServiceProviderFactory 源码:

private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
{
	_serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
	return this;
}

所以说是替换了我们的serviceProviderFactory。

原理篇<<从新整理1400篇>>介绍了这个东西,这里再简要介绍一下。

_serviceProviderFactory 通过代理模式下进行的,也就是一个适配过程,那么我们直接看适配器。

其实适配器有一个小的隐藏信息哈。比如说_serviceProviderFactory的命名上看,翻译过来就是serviceProvider的构建工厂,也就是为我们提供serviceProvider的生产工厂。

直接看ServiceFactoryAdapter的源码。

internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter
  {
    private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
    private readonly Func<HostBuilderContext> _contextResolver;
    private Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;

    public ServiceFactoryAdapter(
      IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
    {
      IServiceProviderFactory<TContainerBuilder> serviceProviderFactory1 = serviceProviderFactory;
      if (serviceProviderFactory1 == null)
        throw new ArgumentNullException(nameof (serviceProviderFactory));
      this._serviceProviderFactory = serviceProviderFactory1;
    }

    public ServiceFactoryAdapter(
      Func<HostBuilderContext> contextResolver,
      Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
    {
      Func<HostBuilderContext> func1 = contextResolver;
      if (func1 == null)
        throw new ArgumentNullException(nameof (contextResolver));
      this._contextResolver = func1;
      Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> func2 = factoryResolver;
      if (func2 == null)
        throw new ArgumentNullException(nameof (factoryResolver));
      this._factoryResolver = func2;
    }

    public object CreateBuilder(IServiceCollection services)
    {
      if (this._serviceProviderFactory == null)
      {
        this._serviceProviderFactory = this._factoryResolver(this._contextResolver());
        if (this._serviceProviderFactory == null)
          throw new InvalidOperationException("The resolver returned a null IServiceProviderFactory");
      }
      return (object) this._serviceProviderFactory.CreateBuilder(services);
    }

    public IServiceProvider CreateServiceProvider(object containerBuilder)
    {
      if (this._serviceProviderFactory == null)
        throw new InvalidOperationException("CreateBuilder must be called before CreateServiceProvider");
      return this._serviceProviderFactory.CreateServiceProvider((TContainerBuilder) containerBuilder);
    }
  }

看这个CreateServiceProvider方法,和猜想的一致。再来看下HostBuilder调用情况。

private void CreateServiceProvider()
{
	var services = new ServiceCollection();
#pragma warning disable CS0618 // Type or member is obsolete
	services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
#pragma warning restore CS0618 // Type or member is obsolete
	services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
	services.AddSingleton(_hostBuilderContext);
	// register configuration as factory to make it dispose with the service provider
	services.AddSingleton(_ => _appConfiguration);
#pragma warning disable CS0618 // Type or member is obsolete
	services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
#pragma warning restore CS0618 // Type or member is obsolete
	services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
	services.AddSingleton<IHostLifetime, ConsoleLifetime>();
	services.AddSingleton<IHost, Internal.Host>();
	services.AddOptions();
	services.AddLogging();

	foreach (var configureServicesAction in _configureServicesActions)
	{
		configureServicesAction(_hostBuilderContext, services);
	}

	var containerBuilder = _serviceProviderFactory.CreateBuilder(services);

	foreach (var containerAction in _configureContainerActions)
	{
		containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
	}

	_appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);

	if (_appServices == null)
	{
		throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
	}

	// resolve configuration explicitly once to mark it as resolved within the
	// service provider, ensuring it will be properly disposed with the provider
	_ = _appServices.GetService<IConfiguration>();
}

的却如此。完全证实了这个猜想。

那么serviceProver 用来做什么的呢?

   1: internal class ServiceProvider : IServiceProvider, IDisposable
   2: {
   3:     public ServiceProvider Root { get; private set; }
   4:     public ServiceTable ServiceTable { get; private set; }
   5:     public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { get; private set; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
   6:     public IList<IDisposable> TransientDisposableServices { get; private set; } = new List<IDisposable>();
   7:     public ConcurrentDictionary<IService, object> ResolvedServices { get; private set; } = new ConcurrentDictionary<IService, object>();
   8:     
   9:     public ServiceProvider(IServiceCollection services)
  10:     {
  11:         this.Root         = this;
  12:         this.ServiceTable     = new ServiceTable(services);
  13:     }
  14:  
  15:     public object GetService(Type serviceType)
  16:     {
  17:         Func<ServiceProvider, object> serviceAccessor;
  18:         if (this.RealizedServices.TryGetValue(serviceType, out serviceAccessor))
  19:         {
  20:             return serviceAccessor(this);
  21:         }
  22:  
  23:         IServiceCallSite serviceCallSite = this.GetServiceCallSite(serviceType, new HashSet<Type>());
  24:         if (null != serviceCallSite)
  25:         {
  26:             var providerExpression = Expression.Parameter(typeof(ServiceProvider), "provider");
  27:             this.RealizedServices[serviceType] = Expression.Lambda<Func<ServiceProvider, object>>(serviceCallSite.Build(providerExpression), providerExpression).Compile();
  28:             return serviceCallSite.Invoke(this);
  29:         }
  30:  
  31:         this.RealizedServices[serviceType] = _ => null;
  32:         return null;
  33:     }
  34:  
  35:     public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)
  36:     {
  37:             try
  38:             {
  39:                 if (callSiteChain.Contains(serviceType))
  40:                 {
  41:                     throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type '{0}'", serviceType.FullName);
  42:                 }
  43:                 callSiteChain.Add(serviceType);
  44:  
  45:                 ServiceEntry serviceEntry;
  46:                 if (this.ServiceTable.ServieEntries.TryGetValue(serviceType, 
  47:                     out serviceEntry))
  48:                 {
  49:                     return serviceEntry.Last.CreateCallSite(this, callSiteChain);
  50:                 }
  51:  
  52:                 //省略其他代码
  53:  
  54:                 return null;
  55:             }
  56:             finally
  57:             {
  58:                 callSiteChain.Remove(serviceType);
  59:             }
  60:     }    
  61:  
  62:     public void Dispose()
  63:     {
  64:         Array.ForEach(this.TransientDisposableServices.ToArray(), _ => _.Dispose());
  65:         Array.ForEach(this.ResolvedServices.Values.ToArray(), _ => (_ as IDisposable)?.Dispose());
  66:         this.TransientDisposableServices.Clear();
  67:         this.ResolvedServices.Clear();
  68:     }
  69:     //其他成员
  70: }

看到GetService 是否特别的眼熟?这个就是通过我们的注册信息来产生不同的对象的。

那么Autofac的作用就是替换了我们的ServiceProvider。也就是替换了,往容器注入的方式了。

那么他给我们扩展了什么功能,后面就不介绍具体源码了,应为是实践篇,主要解释用法。

为什么有命名注册的方式呢?有什么痛点呢?

services.AddSingleton<IMySingletonService>(new MySingletonService());
services.AddSingleton<IMySingletonService>(ServiceProvider =>
{
	return new MySingletonService();
});

那么可以肯定一点的是,这时候通过IMySingletonService 可以获取第一个,也可以获取全部。

但是假如我要获取指定的一个呢?那么要给他们加上标记,但是.net core 自带的并没有并没有。

这时候使用autofac。

在startUp 中加入:

public void ConfigureContainer(ContainerBuilder builder)
{
	builder.RegisterType<TestService>().As<ITestService>();
	builder.RegisterType<TestService>().Named<ITestService>("service2");
}

然后在startUp 的Configure 中加入:

this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
Console.WriteLine(testServiceNameDefault.GetHashCode());

var testServiceName2= this.AutofacContainer.ResolveNamed<ITestService>("service2");
Console.WriteLine(testServiceName2.GetHashCode());

发现他们的hashcode并不一致,故而获取了不同对象。

把这个TestService 修改一下:

public interface ITestService
{
       public void ShowAttributeState();
}

public class TestService:ITestService,IDisposable
{
	public AttributeService Attribute { get; set; }

	public void ShowAttributeState()
	{
		Console.WriteLine($"attribute is null?{(Attribute == null ? "true":"false")}");
	}

	public void Dispose()
	{
		Console.WriteLine($"DisposableTestService Disposed:{this.GetHashCode()}");
	}
}

AttributeService 如下:

public class AttributeService
{
}

AttributeService 的注册信息:
然后添加:

services.AddTransient<AttributeService>();
builder.RegisterType<TestService>().As<ITestService>();

注:这里我特意用services 来注册AttributeService 是为了证明autofac 兼容了.net core 原生的注册信息,证明前面的替换serviceProvider 的推导过程,这样我们就可以在我们的老项目中直接使用。

获取信息:

this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
testServiceNameDefault.ShowAttributeState();

TestService的注册信息修改一下:

builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired();

设置自动注入属性,这样就可以了。如果对象属性有注册信息的话,会帮我们自动填充。

aop 注入

aop 如果没怎么看的话,可以简单理解可以理解为拦截器。

一般我们看到属性的方式来写加入拦截器:

[Attribuite]
public void method(){
}

这样是显式注入拦截器,那么autofac 为我们隐式注入。

安装一下Castle.core。然后我们写一个拦截器。

public class TestInterceptor : IInterceptor
{
	public void Intercept(IInvocation invocation)
	{
		Console.WriteLine($"Invocation before Method:{invocation.Method.Name}");
		invocation.Proceed();
		Console.WriteLine($"Invocation after Method:{invocation.Method.Name}");
	}
}

然后我们注册的时候注入拦截器。

安装一下:Autofac.Extras.DynamicProxy

写入注册信息:

builder.RegisterType<TestInterceptor>();
builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired().InterceptedBy(typeof(TestInterceptor)).EnableInterfaceInterceptors();

然后调用:

this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
testServiceNameDefault.ShowAttributeState();

看下结果:

给容器命名

builder.RegisterType<TestService>().As<ITestService>().InstancePerMatchingLifetimeScope("selfScope");

然后调用:

this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
using (var myScope = AutofacContainer.BeginLifetimeScope())
{ 
	var obj = myScope.Resolve<ITestService>();
	Console.WriteLine(obj==null?"true":"false");
}

这里会报错myScope.Resolve();,因为获取不到,指定了指定容器selfScope。

要这样写:

using (var myScope = AutofacContainer.BeginLifetimeScope("selfScope"))
{ 
	var obj = myScope.Resolve<ITestService>();
	Console.WriteLine(obj==null?"true":"false");
}

这么写好像是不能体现出这个容器命名有什么作用。

画一张概念图:

上述是原先.net core 的一个隔离思路。

如果给容器命名的话,相当于每个scope可以继续套娃,起一个隔离作用。

代码演示:

this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
using (var myScope = AutofacContainer.BeginLifetimeScope("selfScope"))
{ 
	var obj = myScope.Resolve<ITestService>();
	using (var myChildScope = myScope.BeginLifetimeScope())
	{
		var obj1 = myChildScope.Resolve<ITestService>();
		Console.WriteLine(obj.GetHashCode());
		Console.WriteLine(obj1.GetHashCode());
	}
}

这种隔离机制做项目的时候就能体现出来,因为可能有几个服务共同用到了某个类,这样解决管理困难问题。

上述只是个人整理,如有问题,望请指出,谢谢。

下一节:配置之盟约。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK