在經過前面的實踐後,利用ABP框架解決簡單的業務需求(增刪改查)基本是沒問題了。然而ABP框架的能力可不止這些,想要更好的使用那些高級特性,就需要了解下ABP框架的結構和工作原理了。
1.框架結構
1.1項目依賴關係
再次回顧下系列文章第一篇中所描述的項目依賴關係。應用層Application和基礎設施層EntityFrameworkCore都依賴於領域層Core,而呈現和分佈式應用層Web又同時依賴於應用層Application和基礎設施層EntityFrameworkCore。
1.2類依賴關係
上面是到目前爲止解決方案中類依賴關係圖,看起來挺複雜的,但其實核心的就是紅色方框中的內容。另外由於ABP框架大量用到依賴注入、模塊系統等特性,有些事實上的依賴關係並沒有在圖中體現。
上面這張圖是簡化過的類依賴關係圖,也是解決方案最開始的狀態。可以看到項目啓動過程中幾個關鍵點,Startup啓動類不用多說,數據庫選項配置DbContextOptionConfigurer、Web項目模塊xxxWebModule和其他項目模塊xxxModule等。
2.啓動流程
2.1 Program.cs
Program.cs的用途自然就是整個項目的入口了,項目啓動就就從這裏開始。在Main函數中構建WebHost並啓動。在構建WebHost時以Startup作爲啓動類。這點和原生的AspNetCore項目是一致的。
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
2.2 Startup.cs
在Startup中有兩個方法,一個是ConfigureServices,輸入參數類型是IServiceCollection,輸出參數類型爲IServiceProvider。在這個方法中首先是使用擴展方法AddAbpDbContext < TDbCOntext >配置數據庫上下文,最後又使用AddAbp< AbpDemoWebModule >對ABP框架和依賴注入進行配置,同時也將AbpDemoWebModule作爲了啓動模塊。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//配置數據上下文
services.AddAbpDbContext<AbpDemoDbContext>(options =>
{
DbContextOptionsConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
});
services.AddControllersWithViews(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
}).AddNewtonsoftJson();
//配置ABP框架及依賴注入
return services.AddAbp<AbpDemoWebModule>(options =>
{
//配置Log4Net日誌
options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
);
});
}
先來看看重點的AddAbp< AbpDemoWebModule >是如何實現的。
public static IServiceProvider AddAbp<TStartupModule>(
this IServiceCollection services,
Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
//創建AbpBootstrapper實例並注入到容器中
AbpBootstrapper abpBootstrapper = AbpServiceCollectionExtensions.AddAbpBootstrapper<TStartupModule>(services, optionsAction);
//配置AspNetCore相關參數,如替換默認服務、添加Abp過濾器等
AbpServiceCollectionExtensions.ConfigureAspNetCore(services, (IIocResolver) abpBootstrapper.IocManager);
//替換容器,將AspNet Core默認的Ioc容器IServiceCollection替換爲CastleWindsor的IocContainer
return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}
Startup中另一個方法是Configure,在這個方法中實現了ABP框架初始化和Web應用的一些常用配置。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.UseAbp(); //初始化ABP框架
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
那麼最關鍵的一行代碼app.UseAbp()到底是如何初始化ABP框架的?通過源代碼可以看到有配置項的引入和一堆檢查。
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);//初始化ABP框架
if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
}
if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
}
核心的一句InitializeAbp()其實就是找到之前的AbpBootstrapper實例初始化。
private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize();//ABP框架初始化
var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
而AbpBootstrapper實例的Initialize()方法纔是真正開始ABP框架的初始化。
public virtual void Initialize()
{
//日誌
ResolveLogger();
try
{
//註冊Bootstrapper
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框架的模塊系統,不同功能域之間以模塊的形式相互依賴。在模塊的初始化和啓動階段,框架從AbpDemoWebModule模塊類開始,不斷循環查找直到遍歷完成所有Module,之後將所有Module按依賴關係進行排序,先後執行預初始操作和初始化操作。這個過程自然也包括領域層AbpDemoCoreModule、應用層AbpDemoApplicationModule、基礎設施層AbpDemoEntityFrameworkCoreModule。
3.小結
經過上面的過程,ABP框架從基礎設施、各個模塊、各項配置都初始化並配置完成,整個項目也順利的啓動起來。不過篇幅有限,只是將ABP框架啓動流程簡要進行了描述,還有很多細節問題沒有講清楚,後面會把這些內容拆分開來說明。