233  
查询码:00000565
ABP框架启动流程解析
作者: 潘帅 于 2020年03月03日 发布在分类 / 人防组 / 人防后端 下,并于 2020年03月03日 编辑
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。



 推荐知识

 历史版本

修改日期 修改人 备注
2020-03-03 17:51:40[当前版本] 潘帅 1.0

  目录
    知识分享平台 -V 4.8.7 -wcp