Abp 源码分析(3):DI 和 Autofac

一、概述

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;
}

参考资料