0.簡要介紹
在 Abp 框架里面,無時無刻不存在依賴注入,關於依賴注入的作用與好處我就不在這里多加贅述了,網上有很多解釋的教程。在 [Abp 源碼分析]一、Abp 框架啟動流程分析 里面已經說過,Abp 本身在框架初始化的時候我們就可以看到它使用 Castle Windsor 將 Asp.Net Core 自帶的 IServiceProvider 替換掉了。
1.大體結構
在 Abp 框架當中,它的依賴注入相關的類型基本上都放在 Abp 項目的 Dependency 文件夾里面,下圖是他們之間的依賴關系:
2 代碼解析
2.1 基本實現
IIocManager
是直接繼承 IIocRegistrar
與 IIocResolver
的一個接口,通過名稱我們就可以看出來他們的作用,IIocRegistrar
內部提供了組件注冊的方法定義,而 IIocResolver
內部則是提供了解析已經注入的組件方法。在 IIocManager
本身則是封裝了一個 Castle Windsor 的 Ioc 容器,定義如下:
/// <summary>
/// This interface is used to directly perform dependency injection tasks.
/// </summary>
public interface IIocManager : IIocRegistrar, IIocResolver, IDisposable
{
/// <summary>
/// Reference to the Castle Windsor Container.
/// </summary>
IWindsorContainer IocContainer { get; }
/// <summary>
/// Checks whether given type is registered before.
/// </summary>
/// <param name="type">Type to check</param>
new bool IsRegistered(Type type);
/// <summary>
/// Checks whether given type is registered before.
/// </summary>
/// <typeparam name="T">Type to check</typeparam>
new bool IsRegistered<T>();
}
那么我們來看看 IIocManager 的具體實現。
方法雖然看起來挺多,不過更多的只是在 Castle Windsor 上面進行了一層封裝而已,可以看到 Register()
這個注冊方法在其內部也是直接調用的 IWindsorContainer.Register()
來進行注入。
那么 Abp 為什么還要再包裝一層呢,因為對外開放的你在使用的時候都使用的是 IIocManager 提供的注冊方法,那么你需要替換 DI 框架的時候可以很快捷的替換掉整個依賴注入框架而不會影響現有代碼。
public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
{
IocContainer.Register(ApplyLifestyle(Component.For(type), lifeStyle));
}
2.2 規約注入
我們重點說一說它的規約注入,什么是規約注入?
在上面的類圖當中,可以看到有一個 IConventionalDependencyRegistrar
接口,並且該接口還擁有四個實現,我們以 BasicConventionalRegistrar
類為例子看看里面做了什么操作。
/// <summary>
/// This class is used to register basic dependency implementations such as <see cref="ITransientDependency"/> and <see cref="ISingletonDependency"/>.
/// </summary>
public class BasicConventionalRegistrar : IConventionalDependencyRegistrar
{
public void RegisterAssembly(IConventionalRegistrationContext context)
{
//Transient
context.IocManager.IocContainer.Register(
Classes.FromAssembly(context.Assembly)
.IncludeNonPublicTypes()
.BasedOn<ITransientDependency>()
.If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
.WithService.Self()
.WithService.DefaultInterfaces()
.LifestyleTransient()
);
//Singleton
context.IocManager.IocContainer.Register(
Classes.FromAssembly(context.Assembly)
.IncludeNonPublicTypes()
.BasedOn<ISingletonDependency>()
.If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
.WithService.Self()
.WithService.DefaultInterfaces()
.LifestyleSingleton()
);
//Windsor Interceptors
context.IocManager.IocContainer.Register(
Classes.FromAssembly(context.Assembly)
.IncludeNonPublicTypes()
.BasedOn<IInterceptor>()
.If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
.WithService.Self()
.LifestyleTransient()
);
}
}
在 BasicConventionalRegistrar
內部,他會掃描傳入的程序集,並且根據類型所繼承的接口來進行自動注入,所以 Abp 定義了兩個輔助注入接口,叫做ITransientDependency
和 ISingletonDependency
,並且在下面還注入了攔截器。
這樣的話,我們自己就不需要頻繁的使用 IIocManager.Register()
方法來手動注入,只需要在自己的實現類或者接口上面,繼承上述兩個接口之一即可。
在 IocManager
內部維護了一個集合 _conventionalRegistrars
。
/// <summary>
/// List of all registered conventional registrars.
/// </summary>
private readonly List<IConventionalDependencyRegistrar> _conventionalRegistrars;
這個集合就是已經存在的規約注冊器,在 AbpKernelModule
的預加載方法里面就使用 AddConventionalRegistrar()
方法來添加了 BasicConventionalRegistrar
注冊器。代碼在 AbpKernelModule.cs 的 45 行:
public override void PreInitialize()
{
IocManager.AddConventionalRegistrar(new BasicConventionalRegistrar());
// 其他代碼
}
之后每當程序調用 IIocManager.RegisterAssemblyByConvention(Assembly assembly)
方法的時候,就會根據傳入的 Assembly 來循環調用存放在集合里面注冊器的 RegisterAssembly()
方法,當然你也可以隨時定義一個 Registrar ,注冊約定你也可以自己來編寫。
public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
{
var context = new ConventionalRegistrationContext(assembly, this, config);
foreach (var registerer in _conventionalRegistrars)
{
registerer.RegisterAssembly(context);
}
if (config.InstallInstallers)
{
IocContainer.Install(FromAssembly.Instance(assembly));
}
}
注:一般來說,每個模塊都會在它的
Initialize
方法當中調用 IocManager.RegisterAssemblyByConvention(),將自己傳入該方法當中來注入當前模塊程序集所有符合規約的組件。
這里值得注意的是 RegisterAssemblyByConvention()
方法還有一個重載 RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
,他將會傳入一個 ConventionalRegistrationConfig
對象,該對象只有一個 bool InstallInstallers
屬性,主要是在注冊的時候告訴 Abp 框架是否使用該程序集內部的 IWindsorInstaller
接口規則。
2.3 初始化過程
吶,首先呢在我們初始化 AbpBootstrapper
的時候,就已經創建好了我們的 IocManager
實例,我們可以來到 AbpBootstrapper.cs 的構造函數有以下代碼:
public IIocManager IocManager { get; }
private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
{
Check.NotNull(startupModule, nameof(startupModule));
var options = new AbpBootstrapperOptions();
optionsAction?.Invoke(options);
if (!typeof(AbpModule).GetTypeInfo().IsAssignableFrom(startupModule))
{
throw new ArgumentException($"{nameof(startupModule)} should be derived from {nameof(AbpModule)}.");
}
StartupModule = startupModule;
IocManager = options.IocManager;
PlugInSources = options.PlugInSources;
_logger = NullLogger.Instance;
if (!options.DisableAllInterceptors)
{
AddInterceptorRegistrars();
}
}
可以看到在 new 了一個 AbpBootstrapperOptions
對象,並且在第 17 行將 options 創建好的 IocManager
賦值給 AbpBootstrapper
本身的 IocManager
屬性。
那么在 options 內部是如何創建 IIocManager 的呢?
public AbpBootstrapperOptions()
{
IocManager = Abp.Dependency.IocManager.Instance;
PlugInSources = new PlugInSourceList();
}
可以看到他直接是使用的 IocManager 這個類所提供的一個靜態實例。
也就是在 IocManager 類里面他有一個靜態構造函數:
static IocManager()
{
Instance = new IocManager();
}
就是這種操作,之后在 IocManager 的構造函數里面呢就將自己再注冊到了 Castle Windsor 的容器里面,這樣其他的組件就可以直接注入使用 IIocManager
了。
public IocManager()
{
IocContainer = new WindsorContainer();
_conventionalRegistrars = new List<IConventionalDependencyRegistrar>();
//Register self!
IocContainer.Register(
Component.For<IocManager, IIocManager, IIocRegistrar, IIocResolver>().UsingFactoryMethod(() => this)
);
}
我們可以回顧一下在替換 Asp.Net Core 自身的 Ioc 容器的時候,在使用的 CreateServiceProvider
就是 Castle Windsor 提供的 IocContainer
對象,該對象就是我們上文在 AbpBootstrapperOptions
里面創建的靜態實例。
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction);
ConfigureAspNetCore(services, abpBootstrapper.IocManager);
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}
3.初始化流程圖
總的來說呢,整個 Abp 框架的依賴注入相關的初始化流程圖就像這樣。