zoukankan      html  css  js  c++  java
  • [Abp vNext 源码分析]

    一、简要说明

    本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的。总的来说 ,Abp vNext 比起 ABP 框架更加精简。因为在 vNext 版本当中,原来归属于 Abp 库的许多内置的基本组件 (组织单元、拦截器等) 被拆分成了单独的模块,这样我们来看它整个启动流程就更加地直观清晰。

    二、源码分析

    要分析其源码,我这里是从他官方的 Demo 模板入手的,你可以在 https://abp.io 上构建你自己的模板项目。工具上我使用的是 Jetbrains 家的 Rider,配置好符号服务器(External Symbols Server),我们就能够直接调试其底层源码。(因为 Abp vNext 项目使用了 Source Link)

    2.1 Startup 文件的入口点

    这里我选择的项目是 Web API,直接来到其 Startup.cs 文件,我们就可以看到在 Startup 类当中的 Configure()ConfigureService() 方法内部我们注入并启用了 Abp vNext 框架。

    public class Startup
    {
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            // 注入 Abp 相关的服务。
            services.AddApplication<DemoAppModule>(options =>
                                                   {
                                                       options.UseAutofac();
                                                   });
            
            // 接管自带的 IoC Container。
            return services.BuildServiceProviderFromFactory();
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            // 配置 ASP.NET Core Mvc 相关参数。
            app.InitializeApplication();
        }
    }
    

    在上面我们可以看到,ABP vNext 在注入服务的时候支持传入一个 Action<AbpApplicationCreationOptions> 委托。上述代码中,这个委托内部使用了 UseAutoFac() 将 AutoFac 的容器注入到了 MS IoC 当中,关于这块代码下文会着重讲解。

    2.2 Abp 服务注册

    在上一节看到的服务注册代码,是通过扩展 IServiceCollection 接口编写的一个扩展方法实现的,在方法内部是通过 AbpApplicationFactory 静态工厂来创建一个 AbpApplicationBase 实例。

    public static class ServiceCollectionApplicationExtensions
    {
    	public static IAbpApplicationWithExternalServiceProvider AddApplication<TStartupModule>(
    		[NotNull] this IServiceCollection services, 
    		[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction = null)
    		where TStartupModule : IAbpModule
    	{
    		return AbpApplicationFactory.Create<TStartupModule>(services, optionsAction);
    	}
    	
    	// ... 其他代码
    }
    

    在这个方法当中,通过名字 WithExternalServiceProvider 我们就知道,这个 Applictaion 是依赖于外部的 IServiceProvider 实例。

    提示:

    它继承的 AbpApplicationBase 基类还拥有另外一个实现,即 AbpApplicationWithInternalServiceProvider 类型,该类型一般 用于控制台程序,它会在 Abp vNext 框架内自行构建一个 IServiceProvider 对象。

    我们回到之前的代码,在这个 AbpApplicationWithExternalServiceProvider 类型内部的构造方法很简单,只是通过 IServiceCollection 对象把自己注入到了服务集合当中。

    internal class AbpApplicationWithExternalServiceProvider : AbpApplicationBase, IAbpApplicationWithExternalServiceProvider
    {
    	public AbpApplicationWithExternalServiceProvider(
    		[NotNull] Type startupModuleType, 
    		[NotNull] IServiceCollection services, 
    		[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction
    		) : base(
    			startupModuleType, 
    			services, 
    			optionsAction)
    	{
    		// 注入自己到 IoC 当中。
    		services.AddSingleton<IAbpApplicationWithExternalServiceProvider>(this);
    	}
    
    	// 执行框架初始化操作,主要工作是加载模块并执行其初始化方法。
    	public void Initialize(IServiceProvider serviceProvider)
    	{
    		Check.NotNull(serviceProvider, nameof(serviceProvider));
    
    		SetServiceProvider(serviceProvider);
    
    		InitializeModules();
    	}
    }
    

    重点代码在于它的基类构造函数,在基类构造函数当中 Abp vNext 注入了诸多 ASP.NET Core 需要的日志服务、本地化服务等。并且它也抽象出了一个 IModuleLoader,用于辅助我们加载模块。

    internal AbpApplicationBase(
    	[NotNull] Type startupModuleType,
    	[NotNull] IServiceCollection services,
    	[CanBeNull] Action<AbpApplicationCreationOptions> optionsAction)
    {
    	Check.NotNull(startupModuleType, nameof(startupModuleType));
    	Check.NotNull(services, nameof(services));
    
    	// 设置启动模块。
    	StartupModuleType = startupModuleType;
    	Services = services;
    
    	// 添加一个空的对象访问器,该访问器的值会在初始化的时候被赋值。
    	services.TryAddObjectAccessor<IServiceProvider>();
    
    	// 调用用户传入的配置委托。
    	var options = new AbpApplicationCreationOptions(services);
    	optionsAction?.Invoke(options);
    
    	// 注册自己。
    	services.AddSingleton<IAbpApplication>(this);
    	services.AddSingleton<IModuleContainer>(this);
    
    	// 添加日志等基础设施组件。
    	services.AddCoreServices();
    	// 添加核心的 Abp 服务,主要是模块系统相关组件。
    	services.AddCoreAbpServices(this, options);
    
        // 加载模块,并按照依赖关系排序,依次执行他们的生命周期方法。
    	Modules = LoadModules(services, options);
    }
    

    提示:

    这里的对象访问器其实就是一个占位的类型对象,这样方面后面替换其具体实现。例如在上文当中的 IServiceProvider 通过 ObjectAccessor<T> 对象包裹起来,其值是 NULL,但是在后面我们可以根据自己的需要替换其具体的 Value 。

    2.3 替换 IoC 容器

    再回到之前调用 AddApplication<T>() 传递的委托方法,在其内部我们调用了 UseAutofac() 方法。这个方法很简单,内部就只有三行代码。

    这三行代码主要是初始化了一个 AutoFac 的容器构建对象,其次注入 IServiceProviderFactory 和 Abp 的默认实现 AbpAutofacServiceProviderFactory

    public static void UseAutofac(this AbpApplicationCreationOptions options)
    {
    	var builder = new ContainerBuilder();
    	options.Services.AddObjectAccessor(builder);
        // 这里是实例注册。
    	options.Services.AddSingleton((IServiceProviderFactory<ContainerBuilder>) new AbpAutofacServiceProviderFactory(builder));
    }
    

    这个工厂类的就是在构建 IServiceProvider 的时候使用,即 BuildServiceProviderFromFactory() 方法。该方法内部逻辑很简单,就是从已经注册的服务集合(IServiceCollection)当中获得之前注册的工厂类,通过调用工厂类的 CreateServiceProvider() 方法构建 IServiceProvider,并作为返回值替换掉默认的 IoC 容器。

    public static IServiceProvider BuildServiceProviderFromFactory([NotNull] this IServiceCollection services)
    {
    	Check.NotNull(services, nameof(services));
    
    	// 遍历已经注册的类型,找到之前注入的工厂类。
    	foreach (var service in services)
    	{
    		var factoryInterface = service.ImplementationInstance?.GetType()
    			.GetTypeInfo()
    			.GetInterfaces()
    			.FirstOrDefault(i => i.GetTypeInfo().IsGenericType &&
    								 i.GetGenericTypeDefinition() == typeof(IServiceProviderFactory<>));
    
    		if (factoryInterface == null)
    		{
    			continue;
    		}
    
    		// 通过反射调用 IServiceProvider 的构建方法。
    		var containerBuilderType = factoryInterface.GenericTypeArguments[0];
    		return (IServiceProvider)typeof(ServiceCollectionCommonExtensions)
    			.GetTypeInfo()
    			.GetMethods()
    			.Single(m => m.Name == nameof(BuildServiceProviderFromFactory) && m.IsGenericMethod)
    			.MakeGenericMethod(containerBuilderType)
    			.Invoke(null, new object[] { services, null });
    	}
    
    	return services.BuildServiceProvider();
    }
    
    // 这里是另外一个重载方法的定义。
    public static IServiceProvider BuildServiceProviderFromFactory<TContainerBuilder>([NotNull] this IServiceCollection services, Action<TContainerBuilder> builderAction = null)
    {
    	Check.NotNull(services, nameof(services));
    
    	var serviceProviderFactory = services.GetSingletonInstanceOrNull<IServiceProviderFactory<TContainerBuilder>>();
    	if (serviceProviderFactory == null)
    	{
    		throw new AbpException($"Could not find {typeof(IServiceProviderFactory<TContainerBuilder>).FullName} in {services}.");
    	}
    
    	var builder = serviceProviderFactory.CreateBuilder(services);
    	builderAction?.Invoke(builder);
    	return serviceProviderFactory.CreateServiceProvider(builder);
    }
    

    2.3 初始化 Abp 框架

    这里针对 IApplicationBuilder 的扩展是在模块包 Volo.Abp.AspNetCore 当中的,这里仅讲解 ASP.NET Core Mvc 项目是如何处理的。

    public static void InitializeApplication([NotNull] this IApplicationBuilder app)
    {
    	Check.NotNull(app, nameof(app));
    
        // 获取 IApplicationBuilde 的对象访问器,并将其值设置为 app。
    	app.ApplicationServices.GetRequiredService<ObjectAccessor<IApplicationBuilder>>().Value = app;
    	
    	// 获得之前在 ConfigureService 注册的 Provider 类型,并调用其初始化方法。
    	app.ApplicationServices.GetRequiredService<IAbpApplicationWithExternalServiceProvider>().Initialize(app.ApplicationServices);
    }
    

    这里可能会疑惑 ObjectAccessor<IApplicationBuilder> 是在什么时候注入的,其实该类型是在 AbpAspNetCoreModule 模块注册的。

    public class AbpAspNetCoreModule : AbpModule
    {
    	// ... 其他代码
    	public override void ConfigureServices(ServiceConfigurationContext context)
    	{
    		// ... 其他代码
    		context.Services.AddObjectAccessor<IApplicationBuilder>();
    	}
    	// ... 其他代码
    }
    

    接着看初始化方法内部的操作,初始化方法定义是在基类当中,方法名是 InitializeModules() ,在方法内部,通过 IModuleManager 来执行模块的初始化方法。

    protected virtual void InitializeModules()
    {
    	using (var scope = ServiceProvider.CreateScope())
    	{
    		scope.ServiceProvider
    			.GetRequiredService<IModuleManager>()
    			.InitializeModules(new ApplicationInitializationContext(scope.ServiceProvider));
    	}
    }
    

    除了模块的初始化,模块的销毁动作 Abp vNext 好像是没有作处理,你可以挂载 IApplicationLifetime.ApplicationStopping 事件来手动执行模块的销毁方法。

    三、总结

    总体来说 Abp vNext 的启动流程与之前精简了许多,这是因为在新的框架当中将许多基础组件从核心层移除了,用户可以自由选择自己需要加载的组件。IoC 相关的代码则是通过的 Microsoft Dependency 提供的 IServiceProvider/IServiceCollection 进行操作,没有了之前的 IocManager

    四、点击我跳转到文章目录

  • 相关阅读:
    IntelliJ Idea和IntelliJ webstrm 常用快捷键
    mybatis基础学习2---(resultType和resultMap的用法和区别)和setting的用法
    使用观察者模式观察线程的生命周期
    观察者设计模式介绍
    java的内存模型及缓存问题
    一个解释volatile关键字作用的最好的例子
    多线程的waitset详细介绍
    浅谈单例模式
    SimpleThreadPool给线程池增加自动扩充线程数量,以及闲时自动回收的功能
    SimpleThreadPool给线程池增加拒绝策略和停止方法
  • 原文地址:https://www.cnblogs.com/myzony/p/10722480.html
Copyright © 2011-2022 走看看