在上一篇介绍在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系列的主目录。

												

最新文章

  1. [SAP ABAP开发技术总结]ABAP调优——代码优化
  2. redis常用命令小结
  3. sublime配置java编译环境
  4. Android MVP理解
  5. 【转】开启Apache mod_rewrite模块完全解答
  6. 如何在word里面插入目录
  7. 学习总结 JAVA环境配置 及其相应的步骤
  8. 2.opencv图像处理常用操作
  9. 开源织梦(dedecms)快速搬家图文教程
  10. JS+html--实现图片轮播
  11. Redis客户端管理工具,状态监控工具
  12. WAMP环境搭建过程中遇到的种种问题
  13. poj1741Tree 点分治
  14. 【转载】C#将图片以二进制流的方式存入数据库
  15. Flask从入门到精通
  16. Python3使用AES加密的库函数PyCrypto、PyCryptodome
  17. dell R740在安装完Esxi6.0U3之后出现存储器警告
  18. hdu3001(状压dp,三进制)
  19. 剑指offer四十一之和为S的连续正数序列
  20. redis源码分析——aofrewrite

热门文章

  1. HTML5跳转页面并传值以及localStorage的用法
  2. Vue脚手架搭建项目
  3. arcgis api 3.x for js 共享干货系列之一自写算法实现地图量算工具(附源码下载)
  4. 解决nginx配置负载均衡时invalid host in upstream报错
  5. 查询SQLSERVER执行过的SQL记录(历史查询记录)
  6. 优秀代码摘录片段一:LinkedList中定位index时使用折半思想
  7. RN开发中的报错以及告警
  8. No module named &quot;Crypto&quot;,如何安装Python三方模块Crypto
  9. myapp——自动生成小学四则运算题目的命令行程序(侯国鑫 谢嘉帆)
  10. js 条件判断