在上一篇介紹在StartUp類中的ConfigureService()中的AddAbp方法后我們再來重點說一說在Configure()方法中的UserAbp()方法,還是和前面的一樣我們來通過代碼來進行一步步分析。
public static class AbpApplicationBuilderExtensions
{
public static void UseAbp(this IApplicationBuilder app)
{
app.UseAbp(null);
}
public static void UseAbp([NotNull] this IApplicationBuilder app, Action<AbpApplicationBuilderOptions> optionsAction)
{
Check.NotNull(app, nameof(app));
var options = new AbpApplicationBuilderOptions();
optionsAction?.Invoke(options);
if (options.UseCastleLoggerFactory)
{
app.UseCastleLoggerFactory();
}
InitializeAbp(app);
if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
}
if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
}
public static void UseEmbeddedFiles(this IApplicationBuilder app)
{
app.UseStaticFiles(
new StaticFileOptions
{
FileProvider = new EmbeddedResourceFileProvider(
app.ApplicationServices.GetRequiredService<IIocResolver>()
)
}
);
}
private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize();
var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
public static void UseCastleLoggerFactory(this IApplicationBuilder app)
{
var castleLoggerFactory = app.ApplicationServices.GetService<Castle.Core.Logging.ILoggerFactory>();
if (castleLoggerFactory == null)
{
return;
}
app.ApplicationServices
.GetRequiredService<ILoggerFactory>()
.AddCastleLogger(castleLoggerFactory);
}
public static void UseAbpRequestLocalization(this IApplicationBuilder app, Action<RequestLocalizationOptions> optionsAction = null)
{
var iocResolver = app.ApplicationServices.GetRequiredService<IIocResolver>();
using (var languageManager = iocResolver.ResolveAsDisposable<ILanguageManager>())
{
var supportedCultures = languageManager.Object
.GetLanguages()
.Select(l => CultureInfo.GetCultureInfo(l.Name))
.ToArray();
var options = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
var userProvider = new AbpUserRequestCultureProvider();
//0: QueryStringRequestCultureProvider
options.RequestCultureProviders.Insert(1, userProvider);
options.RequestCultureProviders.Insert(2, new AbpLocalizationHeaderRequestCultureProvider());
//3: CookieRequestCultureProvider
options.RequestCultureProviders.Insert(4, new AbpDefaultRequestCultureProvider());
//5: AcceptLanguageHeaderRequestCultureProvider
optionsAction?.Invoke(options);
userProvider.CookieProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
userProvider.HeaderProvider = options.RequestCultureProviders.OfType<AbpLocalizationHeaderRequestCultureProvider>().FirstOrDefault();
app.UseRequestLocalization(options);
}
}
public static void UseAbpSecurityHeaders(this IApplicationBuilder app)
{
app.UseMiddleware<AbpSecurityHeadersMiddleware>();
}
}
在這個方法中首先也是檢查傳入的IApplicationBuilder是否為null,如果為空則拋出異常,在檢查完之后會創建一個AbpApplicationBuilderOptions類的實例,我們來看看在這個類中做了哪些事情?
public AbpApplicationBuilderOptions()
{
UseCastleLoggerFactory = true;
UseAbpRequestLocalization = true;
UseSecurityHeaders = true;
}
這里比較簡單就是對定義的幾個屬性進行初始化操作。在完成初始化操作后,首先就是定義ABP的創建Logger的工廠類,通過這個工廠類創建Logger的實例,這個主要是記錄ABP的日志,這里面具體的日志使用的是log4net,具體輸出日志的方式可以在log4net.config里面進行配置。
在配置完成后最重要的一步就是執行InitializeAbp方法,我們來看看這個過程中執行了哪些方法。
private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize();
var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
在這個初始化Abp方法中,首先就是通過app.ApplicationServices.GetRequiredService<AbpBootstrapper>()這個方法來獲取到之前唯一的abpBootstrapper,獲取到這個唯一的實例后就可以調用這個實例的Initialize方法。
/// <summary>
/// Initializes the ABP system.
/// </summary>
public virtual void Initialize()
{
ResolveLogger();
try
{
RegisterBootstrapper();
IocManager.IocContainer.Install(new AbpCoreInstaller());
IocManager.Resolve<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources);
IocManager.Resolve<AbpStartupConfiguration>().Initialize();
_moduleManager = IocManager.Resolve<AbpModuleManager>();
_moduleManager.Initialize(StartupModule);
_moduleManager.StartModules();
}
catch (Exception ex)
{
_logger.Fatal(ex.ToString(), ex);
throw;
}
}
在整個初始化的過程中,第一步就是創建整個ABP中唯一的Logger對象,這個主要是用來記錄系統的日志文件,這個具體創建方法就是通過IoCContainer容器來獲取到之前注入的創建Logger日志的工廠類,然后調用它的Create方法來創建這個記錄日志的對象。
private void ResolveLogger()
{
if (IocManager.IsRegistered<ILoggerFactory>())
{
_logger = IocManager.Resolve<ILoggerFactory>().Create(typeof(AbpBootstrapper));
}
}
在創建完日志之后,第一步就是用於判斷當前的AbpBootrsrtapper類的實例有沒有注入到整個依賴注入容器中,如果沒有注入過則將當前唯一的實例this注入到容器中去。
private void RegisterBootstrapper()
{
if (!IocManager.IsRegistered<AbpBootstrapper>())
{
IocManager.IocContainer.Register(
Component.For<AbpBootstrapper>().Instance(this)
);
}
}
再接下來第一步就是執行IocManager.IocContainer.Install(new AbpCoreInstaller())這一步操作,這一步的主要作用是注冊系統框架級的所有配置類,我們通過實際的代碼來看看,在初始化整個ABP系統時候到底注冊了哪些系統框架級別的配置類。
internal class AbpCoreInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUnitOfWorkDefaultOptions, UnitOfWorkDefaultOptions>().ImplementedBy<UnitOfWorkDefaultOptions>().LifestyleSingleton(),
Component.For<INavigationConfiguration, NavigationConfiguration>().ImplementedBy<NavigationConfiguration>().LifestyleSingleton(),
Component.For<ILocalizationConfiguration, LocalizationConfiguration>().ImplementedBy<LocalizationConfiguration>().LifestyleSingleton(),
Component.For<IAuthorizationConfiguration, AuthorizationConfiguration>().ImplementedBy<AuthorizationConfiguration>().LifestyleSingleton(),
Component.For<IValidationConfiguration, ValidationConfiguration>().ImplementedBy<ValidationConfiguration>().LifestyleSingleton(),
Component.For<IFeatureConfiguration, FeatureConfiguration>().ImplementedBy<FeatureConfiguration>().LifestyleSingleton(),
Component.For<ISettingsConfiguration, SettingsConfiguration>().ImplementedBy<SettingsConfiguration>().LifestyleSingleton(),
Component.For<IModuleConfigurations, ModuleConfigurations>().ImplementedBy<ModuleConfigurations>().LifestyleSingleton(),
Component.For<IEventBusConfiguration, EventBusConfiguration>().ImplementedBy<EventBusConfiguration>().LifestyleSingleton(),
Component.For<IMultiTenancyConfig, MultiTenancyConfig>().ImplementedBy<MultiTenancyConfig>().LifestyleSingleton(),
Component.For<ICachingConfiguration, CachingConfiguration>().ImplementedBy<CachingConfiguration>().LifestyleSingleton(),
Component.For<IAuditingConfiguration, AuditingConfiguration>().ImplementedBy<AuditingConfiguration>().LifestyleSingleton(),
Component.For<IBackgroundJobConfiguration, BackgroundJobConfiguration>().ImplementedBy<BackgroundJobConfiguration>().LifestyleSingleton(),
Component.For<INotificationConfiguration, NotificationConfiguration>().ImplementedBy<NotificationConfiguration>().LifestyleSingleton(),
Component.For<IEmbeddedResourcesConfiguration, EmbeddedResourcesConfiguration>().ImplementedBy<EmbeddedResourcesConfiguration>().LifestyleSingleton(),
Component.For<IAbpStartupConfiguration, AbpStartupConfiguration>().ImplementedBy<AbpStartupConfiguration>().LifestyleSingleton(),
Component.For<IEntityHistoryConfiguration, EntityHistoryConfiguration>().ImplementedBy<EntityHistoryConfiguration>().LifestyleSingleton(),
Component.For<ITypeFinder, TypeFinder>().ImplementedBy<TypeFinder>().LifestyleSingleton(),
Component.For<IAbpPlugInManager, AbpPlugInManager>().ImplementedBy<AbpPlugInManager>().LifestyleSingleton(),
Component.For<IAbpModuleManager, AbpModuleManager>().ImplementedBy<AbpModuleManager>().LifestyleSingleton(),
Component.For<IAssemblyFinder, AbpAssemblyFinder>().ImplementedBy<AbpAssemblyFinder>().LifestyleSingleton(),
Component.For<ILocalizationManager, LocalizationManager>().ImplementedBy<LocalizationManager>().LifestyleSingleton()
);
}
}
這里面包括框架中各種重要的配置,比如實現整個框架插件化的核心管理類IAbpPlugInManager等等。在將這些核心的框架級別的配置類注入IoC容器中以后就是執行后續的各種操作了。首先第一步就是將放置在特定文件夾下面的實現AbpModule的程序集加載到一個唯一的集合中,這個具體是通過IocManager.Resolve<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources)這個來實現的。
緊接着就是執行IocManager.Resolve<AbpStartupConfiguration>().Initialize(),這個方法主要是用於實例化ABP系統中的各種配置,包括:本地化、模塊、驗證、多租戶配置等一系列配置的初始化,這個在后期都會使用到。關於后面的這些內容,這個系列后續會通過不同章節來分析進行分析和總結。
public void Initialize()
{
Localization = IocManager.Resolve<ILocalizationConfiguration>();
Modules = IocManager.Resolve<IModuleConfigurations>();
Features = IocManager.Resolve<IFeatureConfiguration>();
Navigation = IocManager.Resolve<INavigationConfiguration>();
Authorization = IocManager.Resolve<IAuthorizationConfiguration>();
Validation = IocManager.Resolve<IValidationConfiguration>();
Settings = IocManager.Resolve<ISettingsConfiguration>();
UnitOfWork = IocManager.Resolve<IUnitOfWorkDefaultOptions>();
EventBus = IocManager.Resolve<IEventBusConfiguration>();
MultiTenancy = IocManager.Resolve<IMultiTenancyConfig>();
Auditing = IocManager.Resolve<IAuditingConfiguration>();
Caching = IocManager.Resolve<ICachingConfiguration>();
BackgroundJobs = IocManager.Resolve<IBackgroundJobConfiguration>();
Notifications = IocManager.Resolve<INotificationConfiguration>();
EmbeddedResources = IocManager.Resolve<IEmbeddedResourcesConfiguration>();
EntityHistory = IocManager.Resolve<IEntityHistoryConfiguration>();
CustomConfigProviders = new List<ICustomConfigProvider>();
ServiceReplaceActions = new Dictionary<Type, Action>();
}
在這后面就是整個ABP項目中的重中之重了就是關於各個Module的初始化,這里的主要步驟首先就是獲取AbpModuleManager的實例,然后調用該對象的Initialize方法,在執行這個方法時會將整個ABP項目的啟動Module(WebHostModule)作為TStartupModule參數傳遞進去,我們來看看在這個Initialize()方法中執行了哪些重要的事情。
public virtual void Initialize(Type startupModule)
{
_modules = new AbpModuleCollection(startupModule);
LoadAllModules();
}
在這個方法中,首先創建一個AbpModule的集合,然后就開始加載所有的Module了。
private void LoadAllModules()
{
Logger.Debug("Loading Abp modules...");
List<Type> plugInModuleTypes;
var moduleTypes = FindAllModuleTypes(out plugInModuleTypes).Distinct().ToList();
Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total.");
RegisterModules(moduleTypes);
CreateModules(moduleTypes, plugInModuleTypes);
_modules.EnsureKernelModuleToBeFirst();
_modules.EnsureStartupModuleToBeLast();
SetDependencies();
Logger.DebugFormat("{0} modules loaded.", _modules.Count);
}
在這個函數中,第一步就是找到所有的模塊的類型,具體是通過FindAllModuleTypes這個函數來執行的,我們來看看到底是怎么實現的。
private List<Type> FindAllModuleTypes(out List<Type> plugInModuleTypes)
{
plugInModuleTypes = new List<Type>();
var modules = AbpModule.FindDependedModuleTypesRecursivelyIncludingGivenModule(_modules.StartupModuleType);
foreach (var plugInModuleType in _abpPlugInManager.PlugInSources.GetAllModules())
{
if (modules.AddIfNotContains(plugInModuleType))
{
plugInModuleTypes.Add(plugInModuleType);
}
}
return modules;
}
這里跳過一些步驟,直接看最核心的部分,執行到內部的一個AddModuleAndDependenciesRecursively方法,這個方法是一個迭代方法,在添加每一個Module時首先查找當前Module的依賴Module,如果當前Module已經添加到之前的集合中那么就不再重復添加。
private static void AddModuleAndDependenciesRecursively(List<Type> modules, Type module)
{
if (!IsAbpModule(module))
{
throw new AbpInitializationException("This type is not an ABP module: " + module.AssemblyQualifiedName);
}
if (modules.Contains(module))
{
return;
}
modules.Add(module);
var dependedModules = FindDependedModuleTypes(module);
foreach (var dependedModule in dependedModules)
{
AddModuleAndDependenciesRecursively(modules, dependedModule);
}
}
這里面有一個重點的方法FindDependedModuleTypes方法,我們也來看看其內部的實現。
/// <summary>
/// Finds direct depended modules of a module (excluding given module).
/// </summary>
public static List<Type> FindDependedModuleTypes(Type moduleType)
{
if (!IsAbpModule(moduleType))
{
throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
}
var list = new List<Type>();
if (moduleType.GetTypeInfo().IsDefined(typeof(DependsOnAttribute), true))
{
var dependsOnAttributes = moduleType.GetTypeInfo().GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>();
foreach (var dependsOnAttribute in dependsOnAttributes)
{
foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes)
{
list.Add(dependedModuleType);
}
}
}
return list;
}
在這個方法中我們首先判斷當前類型當前moduleType是否定義了DependsOn的自定義屬性,如果定義了這個這個自定義屬性,那么首先獲取定義的這些Module,然后再將這些ModuleType添加到一個集合中,這樣就能夠獲取到一個ModuleType所依賴的所有其他ModuleType,通過上面的兩個過程能夠將整個ABP系統中 所有的AbpModule都加載到了一個集合中,而且不會重復。
后面接着執行AbpModuleManager中的StartModules()方法,這個方法中會執行一系列的操作。
public virtual void StartModules()
{
var sortedModules = _modules.GetSortedModuleListByDependency();
sortedModules.ForEach(module => module.Instance.PreInitialize());
sortedModules.ForEach(module => module.Instance.Initialize());
sortedModules.ForEach(module => module.Instance.PostInitialize());
}
分析到這里的時候我們可以看到我們非常熟悉的PreInitialze()、Initialize()、PostInitialize()方法了,在執行這三個虛方法之前,首先要將這些加載到的Module進行一個排序,具體排序規則我們稱之為拓撲排序,就是如果A模塊加載依賴於B模塊,那么B模塊就排在A模塊的前面,並執行這三個過程。我們來分析一下GetSortedModuleListByDependency這個方法的具體實現。
public List<AbpModuleInfo> GetSortedModuleListByDependency()
{
var sortedModules = this.SortByDependencies(x => x.Dependencies);
EnsureKernelModuleToBeFirst(sortedModules);
EnsureStartupModuleToBeLast(sortedModules, StartupModuleType);
return sortedModules;
}
在這里涉及到三類不同的模塊,首先是AbpKenelModule,這個是Abp中最核心的Module、第二類就是普通的業務實現相關的Module、第三類是StartupModule,這個模塊會最后進行加載,關於模塊之間的先后排列順序我們可以來通過幾個資料來進行了解,首先我們來看看SortByDependencies這個方法的實現。
public static class ListExtensions
{
/// <summary>
/// Sort a list by a topological sorting, which consider their dependencies
/// </summary>
/// <typeparam name="T">The type of the members of values.</typeparam>
/// <param name="source">A list of objects to sort</param>
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <returns></returns>
public static List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
{
/* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp
* http://en.wikipedia.org/wiki/Topological_sorting
*/
var sorted = new List<T>();
var visited = new Dictionary<T, bool>();
foreach (var item in source)
{
SortByDependenciesVisit(item, getDependencies, sorted, visited);
}
return sorted;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T">The type of the members of values.</typeparam>
/// <param name="item">Item to resolve</param>
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <param name="sorted">List with the sortet items</param>
/// <param name="visited">Dictionary with the visited items</param>
private static void SortByDependenciesVisit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
{
bool inProcess;
var alreadyVisited = visited.TryGetValue(item, out inProcess);
if (alreadyVisited)
{
if (inProcess)
{
throw new ArgumentException("Cyclic dependency found! Item: " + item);
}
}
else
{
visited[item] = true;
var dependencies = getDependencies(item);
if (dependencies != null)
{
foreach (var dependency in dependencies)
{
SortByDependenciesVisit(dependency, getDependencies, sorted, visited);
}
}
visited[item] = false;
sorted.Add(item);
}
}
}
除了代碼注釋中的這個URL涉及到的參考資料以外,可以點擊這里參考這篇文章來進一步理解什么是拓撲排序規則。
在這里我們已經基本分析完了整個UseAbp的過程,這個在整個方法中最核心的就是找到所有的Module並進行拓撲排序,排序完成后一次執行定義在基類中的虛方法,最后在UseAbp中涉及的一個部分就是本地化等相關的內容,這個不是整個ABP項目中的核心,但是對整個ABP項目是一個非常重要的補充,這里我們僅僅貼出實現代碼,不做具體分析。
if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
}
if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
這里面UseAbpRequestLocalization()這個方法的實現如下所示。
public static void UseAbpRequestLocalization(this IApplicationBuilder app, Action<RequestLocalizationOptions> optionsAction = null)
{
var iocResolver = app.ApplicationServices.GetRequiredService<IIocResolver>();
using (var languageManager = iocResolver.ResolveAsDisposable<ILanguageManager>())
{
var supportedCultures = languageManager.Object
.GetLanguages()
.Select(l => CultureInfo.GetCultureInfo(l.Name))
.ToArray();
var options = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
};
var userProvider = new AbpUserRequestCultureProvider();
//0: QueryStringRequestCultureProvider
options.RequestCultureProviders.Insert(1, userProvider);
options.RequestCultureProviders.Insert(2, new AbpLocalizationHeaderRequestCultureProvider());
//3: CookieRequestCultureProvider
options.RequestCultureProviders.Insert(4, new AbpDefaultRequestCultureProvider());
//5: AcceptLanguageHeaderRequestCultureProvider
optionsAction?.Invoke(options);
userProvider.CookieProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
userProvider.HeaderProvider = options.RequestCultureProviders.OfType<AbpLocalizationHeaderRequestCultureProvider>().FirstOrDefault();
app.UseRequestLocalization(options);
}
}
上面就是對整個ABP項目的模塊加載及初始化過程做的一個主要的分析,但是在理解這篇文章之前,最好先看之前的一篇文章,從而讓自己對整個ABP模塊加載有一個更加清楚的認識和理解。
最后,點擊這里返回整個ABP系列的主目錄。
