ABPvnext源码分析 (一):核心模块Abp.Core
写在前面:
ABPvnext(v2)在github开源地址为https://github.com/abpframework/abp,该项目是ABP项目基于netcore版本的第二代实现。他基于netcore3.0,相对于v1,更轻量级,面向微服务等现代网络架构,是学习netcore,学习架构设计的很好的素材。
本系列记录ABPvnext源码学习的详细内容,基于的版本是v1.0正式版。
核心模块(Core模块):ABP的Core模块为Volo.Abp.Core程序包,可以通过nuget安装使用,大家也可以在github查看其源码(framework/src/Volo.Abp.Core)。Core模块整体看来分为两大部分。一部分为一些基础的帮助函数,大部分为无依赖的独立的扩展方法。由于这部分代码相互牵扯很少,相信读者很容易看懂,本篇直接忽略。本篇关注的是另一部分, DI封装以及相关的一些基础功能(这块代码主要在framework/src/Volo.Abp.Core/Volo/)。
Core模块DI相关部分主要是实现了模块管理功能,其主要参考了Autofac模块管理的实现。我想之所以重新实现一遍没有直接依赖Autofac是为了和具体的DI依赖库相隔离,做一层中间层(ABP的DI实现)。代码的其他部分依赖于中间层而不依赖于Autofac,这样可以基于不同的DI层或者就使用aspnetcore默认的DI层。
ABP的Core模块提供了哪些具体的核心功能:
(我们约定把DI中进行注册的类别叫服务,获取的服务实例叫组件)
1.核心服务注册: 包括core核心服务及Abp核心服务注册。 core核心服务包含日志服务,国际化服务以及选项服务;Abp核心服务主要包含的模块管理相关的一些基础服务
2.module注册:解决方案中每个项目添加一个abp module类,其中进行该项目的一些服务配置,模块间依赖配置(模块间依赖基本和项目间依赖一致)
3.规约服务注册(Dependency注解,ISingletonDependency,ITransientDependency,IScopedDependency接口,ExposeServices注解)
4.模块加载 加载模块并按照依赖关系排序,依次执行他们的生命周期方法。
5.动态代理功能: Core模块定义了ABP中代理的一些基础接口,这里并没有相关的具体代理实现依赖。ABP提供了一个具体依赖的实现:在Volo.Abp.Autofac及Volp.Abp.CastleCore包中,上层包中主要使用的是这个实现。
在我们分析Core源码时,如果能把这几块核心功能的相关代码搞清楚,整个Abp.Core的实现也就没有什么难点了。
另外,Core项目可以单独使用。我们查看Volo.Abp.Core的包依赖,发现并没有额外的三方依赖。我们完全可以把Core看成Autofac一个阉割简易的但完全足够使用的版本。读懂Core的源码,抛弃Autofac的一些不常用的复杂的功能,直接使用Abp.Core做为DI库,本身也是个不错的选择。
正菜开始:
首先我们从TestCase中找一段abp的使用起手式,如下:
[Fact] public void Should_Use_Empty_ConfigurationRoot_By_Default() { using (var application = AbpApplicationFactory.Create<IndependentEmptyModule>()) //ABP模块(数据,new 没有行为)=>ABP Application new的时候完成相关服务注册 { var services = application.Services; var configuration1 = application.Services.GetConfiguration(); configuration1.ShouldNotBeNull(); application.Initialize();// 初始化模块, 模块服务已经注入DI,这里执行模块的INIT HOOK方法 var configuration2 = ResolveConfiguration(application); configuration2.ShouldBe(configuration1); } }
通过 AbpApplicationFactory 获取 abpApplication,其Services属性就是注册了各种初始服务的seviceCollection。 abpApplication.Initialize() 可以执行app的初始化工作。
我们继续跟踪这个AbpApplicationFactory.Create<IndependentEmptyModule>(),其内部调用的是
internal AbpApplicationBase( //构造的时候就会注册相关服务,加载各个abp模块 [NotNull] Type startupModuleType, [NotNull] IServiceCollection services, //services是构造时传入的 [CanBeNull] Action<AbpApplicationCreationOptions> optionsAction) { Check.NotNull(startupModuleType, nameof(startupModuleType)); Check.NotNull(services, nameof(services)); StartupModuleType = startupModuleType; Services = services; services.TryAddObjectAccessor<IServiceProvider>(); //ObjectAccessor采用头插法,放入其中的查找较快。 AddObjectAccessor一注册注册一对儿:ObjectAccessor;IObjectAccessor var options = new AbpApplicationCreationOptions(services); //new的一个空对象 这和下面一句是一个经典套路 optionsAction?.Invoke(options); //调用Action services.AddSingleton<IAbpApplication>(this); //两个服务IAbpApplication,IModuleContainer都是AbpApplicationBase对象本身 services.AddSingleton<IModuleContainer>(this); services.AddCoreServices();//核心基本服务 Options Logging Localization services.AddCoreAbpServices(this, options); //ABP核心模块 主要是模块系统相关组件。 Modules = LoadModules(services, options); //加载模块并按照依赖关系排序,依次执行他们的生命周期方法。 IMPORTANT }
参数services是一个new的空的 ServiceCollection。
里面重要的方法调用是
services.AddCoreAbpServices(this, options);
LoadModules(services, options);
我们分别展开来看:
internal static void AddCoreAbpServices(this IServiceCollection services, //添加ABP相关服务 IAbpApplication abpApplication, //需要这个做输入IAbpApplication代表ABP配置 AbpApplicationCreationOptions applicationCreationOptions) //读取Configuration配置 { var moduleLoader = new ModuleLoader(); //几个关键类型ModuleLoader, AssemblyFinder,TypeFinder var assemblyFinder = new AssemblyFinder(abpApplication); //StartupModules=>Modules=>Assemblies var typeFinder = new TypeFinder(assemblyFinder); //封装了所有程序集中所有的Types if (!services.IsAdded<IConfiguration>()) // 没IConfiguration 服务的话 { services.ReplaceConfiguration( ConfigurationHelper.BuildConfiguration( //生成Configuration对象并注册(默认规则appsetting.json环境变量,命令行参数等) applicationCreationOptions.Configuration ) ); } services.TryAddSingleton<IModuleLoader>(moduleLoader); //moduleLoader有装载方法 services.TryAddSingleton<IAssemblyFinder>(assemblyFinder); services.TryAddSingleton<ITypeFinder>(typeFinder); services.AddAssemblyOf<IAbpApplication>(); //添加Volo.Abp.Core程序集(基于约定方式的,注册程序集中services)IMPORTANT services.Configure<AbpModuleLifecycleOptions>(options => //配置模块声明周期的HOOKS { options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>(); options.Contributors.Add<OnApplicationInitializationModuleLifecycleContributor>(); options.Contributors.Add<OnPostApplicationInitializationModuleLifecycleContributor>(); options.Contributors.Add<OnApplicationShutdownModuleLifecycleContributor>(); }); }
services.AddCoreAbpServices(this, options)中先是注册默认的IConfiguration服务(默认行为先读文件再读UserSecrets再读环境变量再读命令行变量,同名的后读的覆盖先读的)
然后添加了IModuleLoader,IAssemblyFinder,ITypeFinder服务。这3个服务封是用来装查询项目以及依赖项目中定义的各种Type的方法的。其原理是通过启动模块StartupModules的依赖关系找到所有的AbpModules,每个AbpModule对应一个Assemblies,然后在每个Assemblies通过反射找到所有定义的Type。
services.AddAssemblyOf<IAbpApplication>(); 添加Volo.Abp.Core程序集(基于约定方式的,注册程序集中services)。他对程序集中每一个Type类型调用规约注册器判断是否注册为服务以及如何注册为服务。
默认实现的规约注册器DefaultConventionalRegistrar是通过识别Dependency注解,ISingletonDependency,ITransientDependency,IScopedDependency接口,ExposeServices注解等判断如何进行服务注册行为。
最后是配置声明模块周期选项,用于模块初始化或关闭时添加钩子行为。LoadModules(services, options)使用默认的ModuleLoader实现加载模块并按照依赖关系排序,依次执行他们的生命周期方法。
public IAbpModuleDescriptor[] LoadModules( IServiceCollection services, Type startupModuleType, PlugInSourceList plugInSources) { Check.NotNull(services, nameof(services)); Check.NotNull(startupModuleType, nameof(startupModuleType)); Check.NotNull(plugInSources, nameof(plugInSources)); var modules = GetDescriptors(services, startupModuleType, plugInSources); modules = SortByDependency(modules, startupModuleType);//排下序,最内层依赖在最前面 ConfigureServices(modules, services); //核心方法,执行每个模块(包括注册模块所在的约定服务以及,执行模块的各个回调) IMPORTANT return modules.ToArray(); }
其中ConfigureServices如下:
protected virtual void ConfigureServices(List<IAbpModuleDescriptor> modules, IServiceCollection services) //可以看成是执行一个个模块 执行顺序:1.执行所有的preConfigure 2.执行每一个模块(先注册该模块约定服务,再执行configureService)3。执行所有的postConfigure { var context = new ServiceConfigurationContext(services); services.AddSingleton(context); //DI注册ServiceConfigurationContext foreach (var module in modules) { if (module.Instance is AbpModule abpModule) { abpModule.ServiceConfigurationContext = context; //装载前设置abpModule.ServiceConfigurationContext } } //PreConfigureServices PreConfigureServices,ConfigureServices,PostConfigureServices只是调用模块HOOK先后顺序不同, 所有PreConfigureServices=>所有ConfigureServices=>所有PostConfigureServices foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices)) { ((IPreConfigureServices)module.Instance).PreConfigureServices(context); } //ConfigureServices foreach (var module in modules) { if (module.Instance is AbpModule abpModule) { if (!abpModule.SkipAutoServiceRegistration) { services.AddAssembly(module.Type.Assembly); //注册模块所在程序集中的约定服务 } } module.Instance.ConfigureServices(context); //先注册所有的约定服务,再执行模块ConfigureServices方法 } //PostConfigureServices foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices)) { ((IPostConfigureServices)module.Instance).PostConfigureServices(context); } foreach (var module in modules) { if (module.Instance is AbpModule abpModule) { abpModule.ServiceConfigurationContext = null; //装载后再清空abpModule.ServiceConfigurationContext } } }
由代码我们可以看出,项目中多个abpModule情况下, 先根据依赖关系依次执行所有模块的PreConfigureServices钩子方法;再对每一个模块先注册模块所在程序集中的约定服务,再
执行ConfigureServices;最后再依次执行所有模块PostConfigureServices方法。 这就是模块加载生命周期的钩子函数调用顺序。
到此,abpApplication的创建就讲完了,我们回忆一下:abpApplication的创建是以启动模块为参数Create创建出来的。在创建的过程完成了IConfigure的注册, Abp模块的注册加载,模块中各个约定服务的注册,模块声明周期钩子函数的执行等操作。最后回到开始时TestCase的例子,abpApplication调用Initialize()执行abp应用的声明周期钩子行为(应用初始化行为)。
public void Initialize() { ServiceScope = Services.BuildServiceProviderFromFactory().CreateScope();// 创建ServiceProvider创建子ServiceProvider SetServiceProvider(ServiceScope.ServiceProvider); InitializeModules(); }
通过BuildServiceProviderFromFactory()创建根ServiceProvider。
public static IServiceProvider BuildServiceProviderFromFactory([NotNull] this IServiceCollection services) //核心方法 IMPORTANT 从services中找serviceproviderfactory services是注册信息 serviceProvider可以理解是services的代理 封装了services;另外,serviceProvider有层次关系而services是层级共享的 { Check.NotNull(services, nameof(services)); foreach (var service in services) //遍历services 注册时可能是 AaaServiceProviderFactory<Bbb>这种,所以不能直接GetService { var factoryInterface = service.ServiceType; if (factoryInterface == null || !factoryInterface.IsGenericType || factoryInterface.GetTypeInfo().GetGenericTypeDefinition() != typeof(IServiceProviderFactory<>)) { continue; } var containerBuilderType = factoryInterface.GenericTypeArguments[0]; //获得 Bbb Bbb是TContainerBuilder return (IServiceProvider)typeof(ServiceCollectionCommonExtensions) //hack,通过反射调用泛型方法 .GetTypeInfo() .GetMethods() .Single(m => m.Name == nameof(BuildServiceProviderFromFactory) && m.IsGenericMethod) .MakeGenericMethod(containerBuilderType) .Invoke(null, new object[] { services, null }); //会调用 services.GetSingletonInstanceOrNull<IServiceProviderFactory<TContainerBuilder>>(); } 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);// ABP默认使用的是Autofac的IServiceProviderFactory实现ContainerBuilder,还是基于之前的services builderAction?.Invoke(builder); return serviceProviderFactory.CreateServiceProvider(builder); //使用工厂类对象生成ServiceProvider }
从上面代码看出 services已注册IServiceProviderFactory<TContainerBuilder>,则使用该组件进行初始化abpApplication,否则根据services生成一个netcore3默认的ServiceProvider初始化abpApplication。
abpCore部分的源码实现如上所述,如果要结合动态代理以及mvc使用,请关注后续的课程。