引入Autofac增强什么能力
基于名称的注入:需要把一个服务按照名称来区分它的不同实现
属性注入:直接把服务注册到某个类的属性里面去,而不需要定义构造函数
子容器:类似原生的scope,但是功能更加丰富
基于动态代理的AOP:当我们需要在服务中注入我们额外的行为的时候
核心扩展点 IServiceProviderFactory<TContainerBuilder>
:第三方的依赖注入容器都是使用这个类来作为拓展点,把自己注入到整个框架里面来,也就是我们在使用依赖注入框架的时候,不需要关注谁家的特性谁家接口时怎么样的,我们直接使用官方核心的定义即可,不需要直接依赖这些框架
集成Autofac
Autofac.Extensions.DependencyInjection
Autofac.Extras.DynamicProxy
代码验证 项目与依赖 创建名字为DependencyInjectionAutofacDemo
的ASP.NET Core
项目,类型为API
通过nuget
引入以下两个包
1 2 Autofac.Extensions.DependencyInjection Autofac.Extras.DynamicProxy
在代码中引入Autofac
在Program.cs
的CreateDefaultBuilder
后面添加以下代码
1 .UseServiceProviderFactory(new AutofacServiceProviderFactory())
UseServiceProviderFactory
用来注册第三方容器的入口
在Startup
中新增ConfigureContainer
方法,代码如下:
1 2 3 public void ConfigureContainer (ContainerBuilder builder ) {}
至此Autofac
框架引入完毕,下面要创建测试服务类
创建测试服务 创建测试服务MyService.cs
类,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using System;namespace DependencyInjectionAutofacDemo.Services { public interface IMyService { void ShowCode ( ) ; } public class MyService : IMyService { public void ShowCode ( ) { Console.WriteLine($"MyService.ShowCode:{GetHashCode()} " ); } } public class MyServiceV2 : IMyService { public MyNameService MyNameService { get ; set ; } public void ShowCode ( ) { Console.WriteLine($"MyServiceV2.ShowCode:{GetHashCode()} ,MyNameService是否为空:{MyNameService==null } " ); } } public class MyNameService { } }
创建测试拦截器MyInterceptor.cs
,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 using System;using Castle.DynamicProxy;namespace DependencyInjectionAutofacDemo.Services { public class MyInterceptor :IInterceptor { public void Intercept (IInvocation invocation ) { Console.WriteLine($"Intercept before,Method:{invocation.Method.Name} " ); invocation.Proceed(); Console.WriteLine($"Intercept after,Method:{invocation.Method.Name} " ); } } }
IInterceptor 是Autofac面向切面最重要的一个接口,他可以把我们的逻辑注入到方法的切面里面去
invocation.Proceed()
是指具体方法的执行,如果这句不执行,就相当于把切面方法拦截了,让具体类的方法不执行
获取Autofac
根容器 在Startup
里新增类型为ILifetimeScope
的AutofacContainer
属性,然后在Configure
方法中为这个属性复制为Autofac
的根容器,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 using System;using Autofac;using Autofac.Extensions.DependencyInjection;using Autofac.Extras.DynamicProxy;using DependencyInjectionAutofacDemo.Services;using Microsoft.AspNetCore.Builder;using Microsoft.AspNetCore.Hosting;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;namespace DependencyInjectionAutofacDemo { public class Startup { public Startup (IConfiguration configuration ) { Configuration = configuration; } public IConfiguration Configuration { get ; } public void ConfigureServices (IServiceCollection services ) { services.AddControllers(); } public void ConfigureContainer (ContainerBuilder builder ) { } public ILifetimeScope AutofacContainer { get ; private set ; } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { this .AutofacContainer = app.ApplicationServices.GetAutofacRoot(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
一般注册服务 在ConfigureContainer
方法中进行服务注册,然后在Configure
方法中获取服务实现对象,调用服务的ShowCode
方法,具体代码如下:
1 2 3 4 5 6 7 8 9 public void ConfigureContainer (ContainerBuilder builder ) { builder.RegisterType<MyService>().As<IMyService>(); } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { var serviceNoName = this .AutofacContainer.Resolve<IMyService>(); serviceNoName.ShowCode(); }
Autofac注册服务与ASP.NET Core
写法相反,先注册实现类,然后再标记这个实现类为哪种类型
运行项目会看到控制台打印了MyService
对象调用ShowCode
方法时候打印的信息,类似信息如下:
1 MyService.ShowCode:16336406
基于名字注册服务 注释掉上一步的测试代码,一样是在ConfigureContainer
方法中进行服务注册,然后在Configure
方法中获取服务实现对象,调用服务的ShowCode
方法,具体代码如下:
1 2 3 4 5 6 7 8 9 public void ConfigureContainer (ContainerBuilder builder ) { builder.RegisterType<MyServiceV2>().Named<IMyService>("service2" ); } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { var service = this .AutofacContainer.ResolveNamed<IMyService>("service2" ); service.ShowCode(); }
运行项目会看到控制台打印了MyServiceV2
对象调用ShowCode
方法时候打印的信息,类似信息如下:
1 MyServiceV2.ShowCode:16336406,MyNameService是否为空:True
属性注入 注释掉上一步的测试代码,在ConfigureContainer
方法中进行服务注册,注意需要先将属性的服务先进行注册,再进行调用方的服务注册,然后一样再Configure
中获取对象,调用ShowCode
方法
1 2 3 4 5 6 7 8 9 10 public void ConfigureContainer (ContainerBuilder builder ) { builder.RegisterType<MyNameService>(); builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired(); } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { var service = this .AutofacContainer.Resolve<IMyService>(); service.ShowCode(); }
运行项目会看到控制台打印了MyServiceV2
对象调用ShowCode
方法时候打印的信息,类似信息如下:
1 MyServiceV2.ShowCode:10309404,MyNameService是否为空:False
可以发现MyNameService
属性已经不为空了,通过属性注入的操作注入到了服务对象中去,打断点进行调试,可以看出MyNameService
类型就是上面注册的类型
AOP切面拦截器 注释掉上一步的测试代码,先在ConfigureContainer
方法中注册拦截器,然后在服务,并指定拦截器为刚刚所注册的拦截器,并且允许接口拦截器生效,获取服务与上一步操作一致
1 2 3 4 5 6 7 8 9 10 public void ConfigureContainer (ContainerBuilder builder ) { builder.RegisterType<MyInterceptor>(); builder.RegisterType<MyServiceV2>().As<IMyService>().PropertiesAutowired().InterceptedBy(typeof (MyInterceptor)).EnableInterfaceInterceptors(); } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { var service = this .AutofacContainer.Resolve<IMyService>(); service.ShowCode(); }
运行项目,可以看到控制台在打印出MyServiceV2
的ShowCode
方法所打印的信息前后,有拦截器打印出来的信息,类似信息如下:
1 2 3 Intercept before,Method:ShowCode MyServiceV2.ShowCode:25116876,MyNameService是否为空:True Intercept after,Method:ShowCode
创建子容器 子容器主要适用于将服务注册进指定名字的容器里,这样只有在创建出指定名字的容器才可获取到服务对象,其他容器无法获得该服务对象,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void ConfigureContainer (ContainerBuilder builder ) { builder.RegisterType<MyNameService>().InstancePerMatchingLifetimeScope("myScope" ); } public void Configure (IApplicationBuilder app, IWebHostEnvironment env ) { using (var myScope = this .AutofacContainer.BeginLifetimeScope("myScope" )) { var service0 = myScope.Resolve<MyNameService>(); using (var scope = myScope.BeginLifetimeScope()) { var service1 = scope.Resolve<MyNameService>(); var service2 = scope.Resolve<MyNameService>(); Console.WriteLine($"service0=service1:{service0==service1} " ); Console.WriteLine($"service1=service2:{service1==service2} " ); } } }
运行代码可看到对象获取成功,并且获取到的对象在作用域内为同一个对象,类似信息如下:
1 2 service0=service1:True service1=service2:True
如果这时候不通过创建指定名字的容器来获得服务对象,会发现代码运行直接报错