Alby's blog

世上没有巧合,只有巧合的假象。

0%

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

一、概述

AbpDI 容器是基于 Microsoft 的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的。因此,它的文档在 Abp 中也是有效的。
Abp 中可以使用 Autofac 替换内置 DI 容器的,以支持属性注入和动态代理(拦截器)。

Abp 提供的服务自动注册功能用内置的 DI 容器也能实现。

二、使用 Autofac 替换内置 DI 容器

我们知道,.Net Core 中将服务的注册和服务的获取分别交给了 ServiceCollectionServiceProvider。如果使用 Autofac,服务的注册继续使用 ServiceCollection。比如 StartupConfigureServices 方法等接收的是 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 对象,实际适配的是 DefaultServiceProviderFactoryHostBuilder.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 也用 AutofacServiceScopeAutofacServiceScopeFactory 对 Autofac 中相应的对象进行了适配。

三、深入 AutofacRegistration

AutofacRegistration 非常重要,它将 ServiceCollection 中注册的服务”填充”到 Autofac 中。

1、Populate 方法

Populate 首先将 AutofacServiceProviderAutofacServiceScopeFactory 注册到 Autofac 中,然后调用 Regisster 私有方法。AutofacServiceScope 总是在 AutofacServiceScopeFactoryCreateScope 方法中直接 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 方法

RegisterAutofacRegistration 的核心方法。它将 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;
}

参考资料