在经过前面的实践后,利用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框架启动流程简要进行了描述,还有很多细节问题没有讲清楚,后面会把这些内容拆分开来说明。