Abp 源码分析(3):DI 和 Autofac
source link: http://blog.tubumu.com/2019/12/26/abp-analysis-03/
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.
Abp
的 DI 容器是基于 Microsoft
的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的。因此,它的文档在 Abp
中也是有效的。Abp
中可以使用 Autofac
替换内置 DI 容器的,以支持属性注入和动态代理(拦截器)。
备注:
Abp
提供的服务自动注册功能用内置的DI
容器也能实现。
二、使用 Autofac 替换内置 DI 容器
我们知道,.Net Core
中将服务的注册和服务的获取分别交给了 ServiceCollection
和 ServiceProvider
。如果使用 Autofac,服务的注册继续使用 ServiceCollection。比如 Startup
的 ConfigureServices
方法等接收的是 IServiceCollection
型的参数。Abp 会将 ServiceColletion 中的服务重新注册入 Autofac,并且使用 AutofacServiceProvider
替换内置 ServiceProvider。
至少有三种方式可将内置的 DI 容器替换为 Autofac。
1、如何替换?
(1)、使用 Startup 的 ConfigureContainer 方法
1
2
3
4
5
6
7
8
9
10
11
// File: Startup.cs
// ConfigureContainer is where you can register things directly
// with Autofac. This runs after ConfigureServices so the things
// here will override registrations made in ConfigureServices.
// Don't build the container; that gets done for you. If you
// need a reference to the container, you need to use the
// "Without ConfigureContainer" mechanism shown later.
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new AutofacModule());
}
(2)、使用 Startup 的 ConfigureServices 方法
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
// File: Startup.cs
// ConfigureServices is where you register dependencies. This gets
// called by the runtime before the Configure method, below.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container.
//
// Note that Populate is basically a foreach to add things
// into Autofac that are in the collection. If you register
// things in Autofac BEFORE Populate then the stuff in the
// ServiceCollection can override those things; if you register
// AFTER Populate those registrations can override things
// in the ServiceCollection. Mix and match as needed.
builder.Populate(services);
builder.RegisterType<MyType>().As<IMyType>();
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
(3)、使用自定义 ServiceProviderFactory
Abp 自定义 ServiceProviderFactory,并且提供两种集成方案。
选择一:自行通过 ServiceProviderFactory 直接创建 ServiceProvider
1
2
3
4
5
6
7
8
9
10
// File: abp\framework\src\Volo.Abp.Autofac\Volo\Abp\AbpAutofacAbpApplicationCreationOptionsExtensions.c
public static class AbpAutofacAbpApplicationCreationOptionsExtensions
{
public static void UseAutofac(this AbpApplicationCreationOptions options)
{
ContainerBuilder builder = new ContainerBuilder();
options.Services.AddObjectAccessor<ContainerBuilder>(builder);
options.Services.AddSingleton<IServiceProviderFactory<ContainerBuilder>>((IServiceProviderFactory<ContainerBuilder>) new AbpAutofacServiceProviderFactory(builder));
}
}
对应的 Startup.cs:
1
2
3
4
5
6
7
8
9
10
11
// File: Startup.cs
// 返回 IServiceProvider
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddApplication<AppModule>(options =>{
options.UseAutofac(); // 集成 Autofac
});
// 因为将 AbpAutofacServiceProviderFactory 注册为了 IServiceProviderFactory<ContainerBuilder> 的单例,所以 BuildServiceProviderFromFactory 可直接通过 ServceCollection 中获取实例。
return services.BuildServiceProviderFromFactory();
}
注意:ConfigureServices 需返回 IServiceProvider 参数以替换 DefaultServiceProvider。详见:
StartupLoader
。
选择二:替换 ServiceProviderFactory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// File: abp\framework\src\Volo.Abp.Autofac\Microsoft\Extensions\Hosting\AbpAutofacHostBuilderExtensions.cs
public static class AbpAutofacHostBuilderExtensions
{
public static IHostBuilder UseAutofac(this IHostBuilder hostBuilder)
{
var containerBuilder = new ContainerBuilder();
return hostBuilder.ConfigureServices((_, services) =>
{
services.AddObjectAccessor(containerBuilder);
})
.UseServiceProviderFactory(new AbpAutofacServiceProviderFactory(containerBuilder)); // 替换默认的 DefaultServiceProviderFactory ,详见 `HostBuilder` 。
}
}
HostBuilder
默认包含一个 ServiceFactoryAdapter
对象,实际适配的是 DefaultServiceProviderFactory
。HostBuilder.UseServiceProviderFactory
方法会构造一个新的ServiceFactoryAdapter
对象适配 AbpAutofacServiceProviderFactory
。
对应的 Program.cs:
1
2
3
4
5
6
7
8
9
// File: Program.cs
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseAutofac() // 集成 Autofac
.UseSerilog();
2、AbpAutofacServiceProviderFactory 类
AbpAutofacServiceProviderFactory
的作用是将注册入 ServiceCollection 中的服务转移入 Autofac 和生产出 AutofacServiceProvider
。
在ContainerBuilder CreateBuilder(IServiceCollection services)
方法中,将之前注册在 ServiceCollection 集合中的服务转移到 Autofac 中。并且启动了属性注入(Property injection)、拦截器(Interceptor)。
在IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
方法中,ContainerBuilder 构建一个 Autofac 容器,然后创建 AutofacServiceProvider 返回。
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
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Extensions\DependencyInjection\AutofacServiceProviderFactory.cs
public class AutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder> {
private readonly Action<ContainerBuilder> _configurationAction;
public AutofacServiceProviderFactory(Action<ContainerBuilder>
configurationAction = null) {
_configurationAction = configurationAction ?? (builder => { });
}
public ContainerBuilder CreateBuilder(IServiceCollection services) {
var builder = new ContainerBuilder();
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Extensions\DependencyInjection\AutofacRegistration.cs
// 将 ServiceCollection 填充入 ContainerBuilder
builder.Populate(services);
_configurationAction(builder);
return builder;
}
public IServiceProvider CreateServiceProvider(ContainerBuilder
containerBuilder) {
if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder));
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
}
备注:将 ServiceProviderFactory、ContainerBuilder 和 ServiceProvider 三者的关系做个可能不太恰当的类比。ServiceProvider 是榨汁机,ContainerBuilder 是组装榨汁机的机器,ServiceProviderFactory 可以生产“组装榨汁机的机器”和通过该机器生产最终的“榨汁机”。榨汁机为我们提供榨汁服务。
扩展:AutofacServiceProviderFactory
对 AutofacRegistration
的深入分析请见下文。
3、AutofacServiceProvider 类
AbpServiceProvider
实际上是 Autofac 的适配器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Extensions\DependencyInjection\AutofacServiceProvider.cs
public class AutofacServiceProvider : IServiceProvider, ISupportRequiredService {
private readonly IComponentContext _componentContext;
public AutofacServiceProvider(IComponentContext componentContext) {
this._componentContext = componentContext;
}
public object GetRequiredService(Type serviceType) {
return this._componentContext.Resolve(serviceType);
}
public object GetService(Type serviceType) {
return this._componentContext.ResolveOptional(serviceType);
}
}
4、AutofacServiceScope 类和 AutofacServiceScopeFactory 类
除了AbpServiceProvider
, Abp 也用 AutofacServiceScope
和 AutofacServiceScopeFactory
对 Autofac 中相应的对象进行了适配。
三、深入 AutofacRegistration
AutofacRegistration
非常重要,它将 ServiceCollection 中注册的服务”填充”到 Autofac 中。
1、Populate 方法
Populate
首先将 AutofacServiceProvider
和 AutofacServiceScopeFactory
注册到 Autofac 中,然后调用 Regisster
私有方法。AutofacServiceScope
总是在 AutofacServiceScopeFactory
的 CreateScope
方法中直接 new 出来,所以不用注册。
1
2
3
4
5
6
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Extensions\DependencyInjection\AutofacRegistration.cs
public static void Populate(this ContainerBuilder builder, IServiceCollection services) {
builder.RegisterType<AutofacServiceProvider>().As<IServiceProvider>();
builder.RegisterType<AutofacServiceScopeFactory>().As<IServiceScopeFactory>();
Register(builder, services);
}
2、Register 方法
Register
是 AutofacRegistration
的核心方法。它将 ServiceCollection 中注册的服务重新注册入 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
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Extensions\DependencyInjection\AutofacRegistration.cs
private static void Register(ContainerBuilder builder, IServiceCollection services) {
var moduleContainer = services.GetSingletonInstance<IModuleContainer>();
var registrationActionList = services.GetRegistrationActionList();
foreach (var service in services) {
if (service.ImplementationType != null) {
// Test if the an open generic type is being registered
var serviceTypeInfo = service.ServiceType.GetTypeInfo();
if (serviceTypeInfo.IsGenericTypeDefinition) {
builder
.RegisterGeneric(service.ImplementationType)
.As(service.ServiceType)
.ConfigureLifecycle(service.Lifetime)
.ConfigureAbpConventions(moduleContainer, registrationActionList);
} else {
builder
.RegisterType(service.ImplementationType)
.As(service.ServiceType)
.ConfigureLifecycle(service.Lifetime)
.ConfigureAbpConventions(moduleContainer, registrationActionList);
}
} else if (service.ImplementationFactory != null) {
var registration = RegistrationBuilder.ForDelegate(service.ServiceType, (context, parameters) => {
var serviceProvider = context.Resolve<IServiceProvider>();
return service.ImplementationFactory(serviceProvider);
})
.ConfigureLifecycle(service.Lifetime)
.CreateRegistration();
//TODO: ConfigureAbpConventions ?
builder.RegisterComponent(registration);
} else {
builder
.RegisterInstance(service.ImplementationInstance)
.As(service.ServiceType)
.ConfigureLifecycle(service.Lifetime);
}
}
}
AbpRegistrationBuilderExtensions.ConfigureAbpConventions
方法启用属性注入、收集拦截器(Interceptor
) 并将之注册入 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
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Builder\AbpRegistrationBuilderExtensions.cs
public static class AbpRegistrationBuilderExtensions
{
public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> ConfigureAbpConventions<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
IModuleContainer moduleContainer,
ServiceRegistrationActionList registrationActionList)
where TActivatorData : ReflectionActivatorData
{
var serviceType = registrationBuilder.RegistrationData.Services.OfType<IServiceWithType>().FirstOrDefault()?.ServiceType;
if (serviceType == null)
{
return registrationBuilder;
}
var implementationType = registrationBuilder.ActivatorData.ImplementationType;
if (implementationType == null)
{
return registrationBuilder;
}
// 启用属性注入
registrationBuilder = registrationBuilder.EnablePropertyInjection(moduleContainer, implementationType);
// 注册后续操作
registrationBuilder = registrationBuilder.InvokeRegistrationActions(registrationActionList, serviceType, implementationType);
return registrationBuilder;
}
// ...
}
属性注入是 Autofac 的可选功能但 Apb 将之开启。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Builder\AbpRegistrationBuilderExtensions.cs
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> EnablePropertyInjection<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
IModuleContainer moduleContainer,
Type implementationType)
where TActivatorData : ReflectionActivatorData
{
//Enable Property Injection only for types in an assembly containing an AbpModule
if (moduleContainer.Modules.Any(m => m.Assembly == implementationType.Assembly))
{
registrationBuilder = registrationBuilder.PropertiesAutowired();
}
return registrationBuilder;
}
在 Abp 模块的 PreConfigureServices
方法中调用 ServiceCollectionRegistrationActionExtensions.OnRegister
扩展方法可以将某些“注册操作(Registration action)”放入全局的 ServiceRegistrationActionList : List<Action<IOnServiceRegistredContext>>
对象中。执行这些“注册操作”将可能导致拦截器添加到 OnServiceRegistredContext
中(如果“注册操作”的确是添加拦截器的话)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Builder\AbpRegistrationBuilderExtensions.cs
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InvokeRegistrationActions<TLimit, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder, ServiceRegistrationActionList registrationActionList, Type serviceType, Type implementationType)
where TActivatorData : ReflectionActivatorData
{
var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType);
foreach (var registrationAction in registrationActionList)
{
// 执行注册操作
registrationAction.Invoke(serviceRegistredArgs);
}
// 如果服务包含任意拦截器
if (serviceRegistredArgs.Interceptors.Any())
{
registrationBuilder = registrationBuilder.AddInterceptors(
serviceType,
serviceRegistredArgs.Interceptors
);
}
return registrationBuilder;
}
如果服务包含任意拦截器,则会创建代理类来拦截。
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
// File: abp\framework\src\Volo.Abp.Autofac\Autofac\Builder\AbpRegistrationBuilderExtensions.cs
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> AddInterceptors<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
Type serviceType,
IEnumerable<Type> interceptors)
where TActivatorData : ReflectionActivatorData
{
if (serviceType.IsInterface)
{
registrationBuilder = registrationBuilder.EnableInterfaceInterceptors();
}
else
{
(registrationBuilder as IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle>)?.EnableClassInterceptors();
}
foreach (var interceptor in interceptors)
{
// 创建代理类来拦截
registrationBuilder.InterceptedBy(
typeof(CastleAbpInterceptorAdapter<>).MakeGenericType(interceptor)
);
}
return registrationBuilder;
}
备注:只有 Abp 模块程序集中的类型才会开启属性注入,这就是 AbpRegistrationBuilderExtensions.ConfigureAbpConventions 方法接收一个 IModuleContainer 型参数的原因。通常 AbpApplication 是一个 Module container 。详见 AbpRegistrationBuilderExtensions.EnablePropertyInjection 方法。
扩展:
1、动态代理,见 CastleAbpInterceptorAdapter 。
2、服务注册前后注册后进行某些操作,见 ServiceRegistrationActionList(ServiceCollectionRegistrationActionExtensions)、IExposedServiceTypesProvider、ExposedServiceExplorer 。
3、ConfigureLifecycle 方法
ConfigureLifecycle
方法比较简单,就是将 ServiceCollection 中注册的服务的生命周期,相应地配置到 Autofac 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// File: abp\framewrok\src\Volo.Abp.Autofac\Autofac\Extensions\DependencyInjection\AutofacRegistration.cs
private static IRegistrationBuilder<object, TActivatorData, TRegistrationStyle> ConfigureLifecycle<TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<object, TActivatorData, TRegistrationStyle> registrationBuilder,
ServiceLifetime lifecycleKind) {
switch (lifecycleKind) {
case ServiceLifetime.Singleton:
registrationBuilder.SingleInstance();
break;
case ServiceLifetime.Scoped:
registrationBuilder.InstancePerLifetimeScope();
break;
case ServiceLifetime.Transient:
registrationBuilder.InstancePerDependency();
break;
}
return registrationBuilder;
}
https://docs.abp.io/zh-Hans/abp/latest/Dependency-Injection
https://autofaccn.readthedocs.io/zh/latest/integration/netcore.html
https://autofaccn.readthedocs.io/zh/latest/integration/aspnetcore.html
https://www.cnblogs.com/myzony/p/10755010.html
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK