ABP VNext 系列:框架启动流程以及依赖注入原理和源码分析

news/2024/10/15 22:00:01

简单介绍 ABP VNext

Github 地址:https://github.com/abpframework/abp
官网文档地址:https://abp.io/docs/latest
官网:https://abp.io/

  • ABP VNext 框架是一个基于 ASP.NET Core 的完整基础架构,也就是我们现在称的 ABP 框架,它遵循软件开发最佳实践和最新技术来创建现代 Web 应用程序和 API。
  • ABP vNext 是 ABP 框架作者所发起的新项目,ABP vNext 框架核心库比 ABP 框架更加精简,因为将原有许多的组件从其核心库抽离成独立的组件。这样开发人员可以更加灵活的选择自己需要的功能进行集成,使项目远离臃肿的库。
  • ABP 提供了基于领域驱动设计原则和模式的完整、模块化和分层的软件架构。它还提供了实现此架构所需的基础架构和指导。
  • ABP 框架提供了许多功能来更轻松地实现实际场景,例如事件总线、后台作业系统、审计日志、BLOB 存储、数据播种、数据过滤等。

框架启动流程

模块化文档:https://abp.io/docs/latest/framework/architecture/modularity/basics

下载源码:https://github.com/abpframework/abp/tree/dev/framework

image

参考构建一个新 ASP.NET Core MVC 的项目:https://abp.io/docs/latest/get-started/empty-aspnet-core-application

image

    public class Program{public static async Task Main(string[] args){var builder = WebApplication.CreateBuilder(args);builder.Host.UseAutofac(); //推荐使用Autofac作为依赖注入框架//注入ABP相关服务await builder.Services.AddApplicationAsync<MCodeAbpWebModule>();var app = builder.Build();//初始化程序await app.InitializeApplicationAsync();await app.RunAsync();}}/// <summary>/// 需要什么模块引入什么模块,才会注入/// </summary>[DependsOn(typeof(AbpAspNetCoreMvcModule),typeof(AbpSwashbuckleModule),typeof(MCodeAbpApplicationModule),typeof(AbpAutofacModule))]public class MCodeAbpWebModule : AbpModule{/// <summary>/// 将你的服务添加到依赖注入系统并配置其他模块的/// </summary>/// <param name="context"></param>/// <returns></returns>public override Task ConfigureServicesAsync(ServiceConfigurationContext context){var service = context.Services;//设置api格式service.AddControllers();//动态ApiConfigure<AbpAspNetCoreMvcOptions>(options =>{options.ConventionalControllers.Create(typeof(MCodeAbpApplicationModule).Assembly, options => options.RemoteServiceName = "v1");//统一前缀options.ConventionalControllers.ConventionalControllerSettings.ForEach(x => x.RootPath = "api/app");});///配置 Swaggerservice.AddAbpSwaggerGen(options =>{var serviceProvider = service.BuildServiceProvider();var mvcOptions = serviceProvider.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>();var mvcSettings = mvcOptions.Value.ConventionalControllers.ConventionalControllerSettings.DistinctBy(x => x.RemoteServiceName);options.SwaggerDoc("v1", new OpenApiInfo { Title = "mcode.abp.api", Version = "v1", Description = "mcode.abp.api" });// 根据分组名称过滤 API 文档(必须) *****options.DocInclusionPredicate((docName, apiDesc) =>{if (apiDesc.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor){var settingOrNull = mvcSettings.Where(x => x.Assembly == controllerActionDescriptor.ControllerTypeInfo.Assembly).FirstOrDefault();if (settingOrNull is not null){return docName == settingOrNull.RemoteServiceName;}}return false;});//配置模型标识,默认type.Name,名称一样,不同明明空间会报错,所以改成FullName,加上命名空间区分options.CustomSchemaIds(type => type.FullName);});return Task.CompletedTask;}/// <summary>///  在应用程序启动时执行代码/// </summary>/// <param name="context"></param>/// <returns></returns>public override Task OnApplicationInitializationAsync(ApplicationInitializationContext context){var app = context.GetApplicationBuilder();app.UseStaticFiles();app.UseRouting();app.UseSwagger();app.UseAbpSwaggerUI(c =>{c.SwaggerEndpoint("/swagger/v1/swagger.json", "mcode.abp.api");});app.UseConfiguredEndpoints();return Task.CompletedTask;}}public class MCodeAbpApplicationModule : AbpModule{}/// <summary>///扩展示例/// </summary>[Route("[controller]/[action]")]public class TestService : ApplicationService{/// <summary>/// 动态Api/// </summary>/// <param name="name"></param>/// <returns></returns>[HttpGet]public string GetHelloWorld(string? name){return name ?? "HelloWord";}}

源码分析

替换 IOC 容器

通过 UseServiceProviderFactory() 方法 ,构建 Abp 自定义的 Autofac 容器工厂,实际上就是做了一层包装

public static class AbpAutofacHostBuilderExtensions
{public static IHostBuilder UseAutofac(this IHostBuilder hostBuilder){//初始化AutoFac容器构建对象var containerBuilder = new ContainerBuilder();return hostBuilder.ConfigureServices((_, services) =>{services.AddObjectAccessor(containerBuilder);})//注册Abp自定义的Autofac容器工厂.UseServiceProviderFactory(new AbpAutofacServiceProviderFactory(containerBuilder));}
}

添加 ABP 应用程序

通过 AbpApplicationFactory 类工厂创建启动模块程序

public async static Task<IAbpApplicationWithExternalServiceProvider> AddApplicationAsync<TStartupModule>([NotNull] this IServiceCollection services,Action<AbpApplicationCreationOptions>? optionsAction = null)where TStartupModule : IAbpModule{return await AbpApplicationFactory.CreateAsync<TStartupModule>(services,  optionsAction);}/// <summary>///  通过 AbpApplicationFactory 创建 ABP 应用程序/// </summary>/// <typeparam name="TStartupModule"></typeparam>/// <param name="services"></param>/// <param name="optionsAction"></param>/// <returns></returns>public async static Task<IAbpApplicationWithExternalServiceProvider> CreateAsync<TStartupModule>([NotNull] IServiceCollection services,Action<AbpApplicationCreationOptions>? optionsAction = null)where TStartupModule : IAbpModule{var app = Create(typeof(TStartupModule), services, options =>{options.SkipConfigureServices = true;optionsAction?.Invoke(options);});//调用配置服务await app.ConfigureServicesAsync();return app;}

创建 ABP 外部服务提供者

AbpApplicationWithExternalServiceProvider 中构造方法只做了了一件事,就是添加自己到服务中去。
核心是继承了 AbpApplicationBase 基类,实例化后会调用基类的构造方法

    /// <summary>/// 创建 ABP 外部服务提供者(实例化应用程序基类)/// </summary>/// <param name="startupModuleType"></param>/// <param name="services"></param>/// <param name="optionsAction"></param>/// <returns></returns>public static IAbpApplicationWithExternalServiceProvider Create([NotNull] Type startupModuleType,[NotNull] IServiceCollection services,Action<AbpApplicationCreationOptions>? optionsAction = null){//会实例化ABP应用程序基类return new AbpApplicationWithExternalServiceProvider(startupModuleType, services, optionsAction);}

初始化 ABP 程序基类

模块的初始化动作是在 AbpApplicationBase 基类开始的,在该基类当中除了注入模块相关的基础设施以外。
还定义了模块的初始化方法,即 LoadModules() 方法,在该方法内部是调用的 IModuleLoader 去执行具体的加载操作

/// <summary>
/// 初始化 Abp 程序
/// </summary>
/// <param name="startupModuleType"></param>
/// <param name="services"></param>
/// <param name="optionsAction"></param>
internal AbpApplicationBase([NotNull] Type startupModuleType,[NotNull] IServiceCollection services,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);//将Services添加到配置中去optionsAction?.Invoke(options);//获取应用程序名称ApplicationName = GetApplicationName(options);//添加当前对象为各种服务实例services.AddSingleton<IAbpApplication>(this);services.AddSingleton<IApplicationInfoAccessor>(this);services.AddSingleton<IModuleContainer>(this);//添加环境变量services.AddSingleton<IAbpHostEnvironment>(new AbpHostEnvironment(){EnvironmentName = options.Environment});//添加日志、选项等services.AddCoreServices();//添加核心Abp服务,以及模块的四个应用程序生命周期services.AddCoreAbpServices(this, options);//读取所有的模块,并按照预加载、初始化、初始化完成的顺序执行其生命周期方法Modules = LoadModules(services, options);//默认为true,不执行if (!options.SkipConfigureServices){ConfigureServices();}
}

添加核心 ABP 服务

AddCoreServices() 方法中是添加包括常规的日志、选项、本地化。
AddCoreAbpServices() 方法则是 添加 Abp 的核心服务,包括各种读取器,配置

    /// <summary>/// 添加核心服务/// </summary>/// <param name="services"></param>internal static void AddCoreServices(this IServiceCollection services){//添加选项services.AddOptions();//添加日志services.AddLogging();//添加本地化services.AddLocalization();}/// <summary>/// 添加核心ABP服务/// </summary>/// <param name="services"></param>/// <param name="abpApplication"></param>/// <param name="applicationCreationOptions"></param>internal static void AddCoreAbpServices(this IServiceCollection services,IAbpApplication abpApplication,AbpApplicationCreationOptions applicationCreationOptions){var moduleLoader = new ModuleLoader();var assemblyFinder = new AssemblyFinder(abpApplication);var typeFinder = new TypeFinder(assemblyFinder);if (!services.IsAdded<IConfiguration>()){services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(applicationCreationOptions.Configuration));}//添加各种读取器和查找器services.TryAddSingleton<IModuleLoader>(moduleLoader);services.TryAddSingleton<IAssemblyFinder>(assemblyFinder);services.TryAddSingleton<ITypeFinder>(typeFinder);services.TryAddSingleton<IInitLoggerFactory>(new DefaultInitLoggerFactory());//添加 AbpApplication所在程序集的类型services.AddAssemblyOf<IAbpApplication>();services.AddTransient(typeof(ISimpleStateCheckerManager<>), typeof(SimpleStateCheckerManager<>));//配置生命周期选项services.Configure<AbpModuleLifecycleOptions>(options =>{options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>();options.Contributors.Add<OnApplicationInitializationModuleLifecycleContributor>();options.Contributors.Add<OnPostApplicationInitializationModuleLifecycleContributor>();options.Contributors.Add<OnApplicationShutdownModuleLifecycleContributor>();});}

读取模块

进入 IModuleLoader 的默认实现 ModuleLoader,在它的 LoadModules() 方法中,基本逻辑如下:

扫描当前应用程序的所有模块类,并构建模块描述对象。
基于模块描述对象,使用拓扑排序算法来按照模块的依赖性进行排序。
排序完成之后,遍历排序完成的模块描述对象,依次执行它们的三个生命周期方法。

    protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options){//通过ModelLoader实例读取模块,LoadModules的时候会将实例添加到容器中return services.GetSingletonInstance<IModuleLoader>().LoadModules(services,StartupModuleType,options.PlugInSources);}/// <summary>/// 读取模块/// </summary>/// <param name="services"></param>/// <param name="startupModuleType"></param>/// <param name="plugInSources"></param>/// <returns></returns>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);return modules.ToArray();}/// <summary>/// 获取模块说明/// </summary>/// <param name="services"></param>/// <param name="startupModuleType"></param>/// <param name="plugInSources"></param>/// <returns></returns>private List<IAbpModuleDescriptor> GetDescriptors(IServiceCollection services,Type startupModuleType,PlugInSourceList plugInSources){var modules = new List<AbpModuleDescriptor>();//查找ModuleFillModules(modules, services, startupModuleType, plugInSources);//添加到当前模块描述对象的 Dependencies 属性SetDependencies(modules);return modules.Cast<IAbpModuleDescriptor>().ToList();}/// <summary>/// 查找所有的模块/// </summary>/// <param name="modules"></param>/// <param name="services"></param>/// <param name="startupModuleType"></param>/// <param name="plugInSources"></param>protected virtual void FillModules(List<AbpModuleDescriptor> modules,IServiceCollection services,Type startupModuleType,PlugInSourceList plugInSources){var logger = services.GetInitLogger<AbpApplicationBase>();//查找到所有的模块//All modules starting from the startup moduleforeach (var moduleType in AbpModuleHelper.FindAllModuleTypes(startupModuleType, logger)){//创建模块说明并添加到容器中去modules.Add(CreateModuleDescriptor(services, moduleType));}//Plugin modulesforeach (var moduleType in plugInSources.GetAllModules(logger)){if (modules.Any(m => m.Type == moduleType)){continue;}modules.Add(CreateModuleDescriptor(services, moduleType, isLoadedAsPlugIn: true));}}

配置服务(注入上下文、执行其生命周期方法)

创建完 Abp 应用程序后会调用 ConfigureServicesAsync() 方法 ,
会执行每个模块中的:ConfigureServices()PreConfigureServices()PostConfigureServices() 方法等

    /// <summary>/// 配置服务/// </summary>/// <returns></returns>/// <exception cref="AbpInitializationException"></exception>public virtual async Task ConfigureServicesAsync(){CheckMultipleConfigureServices();//将服务配置上下文对象添加到容器中var context = new ServiceConfigurationContext(Services);Services.AddSingleton(context);//遍历所有的容器中的模块,并指定上下文对象foreach (var module in Modules){if (module.Instance is AbpModule abpModule){abpModule.ServiceConfigurationContext = context;}}//遍历执行模块中PreConfigureServicesAsync方法(ConfigureServices之前执行)foreach (var module in Modules.Where(m => m.Instance is IPreConfigureServices)){try{await ((IPreConfigureServices)module.Instance).PreConfigureServicesAsync(context);}catch (Exception ex){throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);}}var assemblies = new HashSet<Assembly>();////遍历模块,将所有的模块中需要注入的进行注入foreach (var module in Modules){if (module.Instance is AbpModule abpModule){if (!abpModule.SkipAutoServiceRegistration){foreach (var assembly in module.AllAssemblies){if (!assemblies.Contains(assembly)){Services.AddAssembly(assembly);assemblies.Add(assembly);}}}}try{//执行模块实例中的方法await module.Instance.ConfigureServicesAsync(context);}catch (Exception ex){throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);}}//遍历执行模块中PostConfigureServicesAsync方法(ConfigureServices之后执行)foreach (var module in Modules.Where(m => m.Instance is IPostConfigureServices)){try{await ((IPostConfigureServices)module.Instance).PostConfigureServicesAsync(context);}catch (Exception ex){throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServicesAsync)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);}}//遍历所有的容器中的模块,并清空上下文对象foreach (var module in Modules){if (module.Instance is AbpModule abpModule){abpModule.ServiceConfigurationContext = null!;}}_configuredServices = true;TryToSetEnvironment(Services);}

组件自动注册源码分析

ABP 提供了三种接口:ISingletonDependencyITransientDependencyIScopedDependency 接口的方式进行注入
方便我们的类型/组件自动注册,这三种接口分别对应了对象的 单例、瞬时、范围 生命周期。
只要任何类型/接口实现了以上任意接口,ABP vNext 就会在系统启动时候,将这些对象注册到 IoC 容器当中
也可以通过 DependencyAttribute 特性 去标记服务的类型,指定服务的生命周期
在模块系统调用模块的 ConfigureServicesAsync() 的时候,就会有一个 services.AddAssembly()方法,
他会将模块的所属的程序集传入

        //ConfigureServicesforeach (var module in Modules){if (module.Instance is AbpModule abpModule){//是否跳过服务的自动注册,默认为 falseif (!abpModule.SkipAutoServiceRegistration){foreach (var assembly in module.AllAssemblies){if (!assemblies.Contains(assembly)){//注入当前程序集Services.AddAssembly(assembly);assemblies.Add(assembly);}}}}try{module.Instance.ConfigureServices(context);}catch (Exception ex){throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);}}

通过获取规则注册器进行注册,Abp 框架中自带默认的注册器 DefaultConventionalRegistrar ,也可以自定义注册器
通过实现 ConventionalRegistrarBase 抽象类去自定义

    public static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly){///获得所有注册器,然后调用注册器的 AddAssembly 方法注册类型。foreach (var registrar in services.GetConventionalRegistrars()){registrar.AddAssembly(services, assembly);}return services;}//抽象基类public abstract class ConventionalRegistrarBase : IConventionalRegistrar
{/// <summary>////// </summary>/// <param name="services"></param>/// <param name="assembly"></param>public virtual void AddAssembly(IServiceCollection services, Assembly assembly){//获得程序集内的所有类型,过滤掉抽象类和泛型类型。var types = AssemblyHelper.GetAllTypes(assembly).Where(type => type != null &&type.IsClass &&!type.IsAbstract &&!type.IsGenericType).ToArray();AddTypes(services, types);}//添加类型public virtual void AddTypes(IServiceCollection services, params Type[] types){foreach (var type in types){AddType(services, type);}}public abstract void AddType(IServiceCollection services, Type type);
}
  • 通过的默认注册器进行注册
public class DefaultConventionalRegistrar : ConventionalRegistrarBase
{/// <summary>/// 添加类型/// </summary>/// <param name="services"></param>/// <param name="type"></param>public override void AddType(IServiceCollection services, Type type){//如果标记了:DisableConventionalRegistrationAttribute特性的,不进行注入if (IsConventionalRegistrationDisabled(type)){return;}//获取Dependency依赖特性var dependencyAttribute = GetDependencyAttributeOrNull(type);//获取生命周期var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);if (lifeTime == null){return;}//转成服务标识var exposedServiceAndKeyedServiceTypes = GetExposedKeyedServiceTypes(type).Concat(GetExposedServiceTypes(type).Select(t => new ServiceIdentifier(t))).ToList();//生成服务TriggerServiceExposing(services, type, exposedServiceAndKeyedServiceTypes);foreach (var exposedServiceType in exposedServiceAndKeyedServiceTypes){var allExposingServiceTypes = exposedServiceType.ServiceKey == null? exposedServiceAndKeyedServiceTypes.Where(x => x.ServiceKey == null).ToList(): exposedServiceAndKeyedServiceTypes.Where(x => x.ServiceKey?.ToString() == exposedServiceType.ServiceKey?.ToString()).ToList();//创建服务描述var serviceDescriptor = CreateServiceDescriptor(type,exposedServiceType.ServiceKey,exposedServiceType.ServiceType,allExposingServiceTypes,lifeTime.Value);if (dependencyAttribute?.ReplaceServices == true){services.Replace(serviceDescriptor);}else if (dependencyAttribute?.TryRegister == true){services.TryAdd(serviceDescriptor);}else{services.Add(serviceDescriptor);}}}
}
  • GetLifeTimeOrNull 方法中 获取生命周期
    /// <summary>/// 获取生命周期/// </summary>/// <param name="type"></param>/// <param name="dependencyAttribute"></param>/// <returns></returns>protected virtual ServiceLifetime? GetLifeTimeOrNull(Type type, DependencyAttribute? dependencyAttribute){// 特性中设置了生命周期或者继承了接口return dependencyAttribute?.Lifetime ?? GetServiceLifetimeFromClassHierarchy(type) ?? GetDefaultLifeTimeOrNull(type);}/// <summary>/// 从接口中获取生命周期/// </summary>/// <param name="type"></param>/// <returns></returns>protected virtual ServiceLifetime? GetServiceLifetimeFromClassHierarchy(Type type){if (typeof(ITransientDependency).GetTypeInfo().IsAssignableFrom(type)){return ServiceLifetime.Transient;}if (typeof(ISingletonDependency).GetTypeInfo().IsAssignableFrom(type)){return ServiceLifetime.Singleton;}if (typeof(IScopedDependency).GetTypeInfo().IsAssignableFrom(type)){return ServiceLifetime.Scoped;}return null;}

初始化 ABP 应用程序

  • 通过 await app.InitializeApplicationAsync(); 方法会初始化应用程序,然后执行每一个模块中的生命周期的方法
    /// <summary>/// 异步初始化 ABP 应用程序/// </summary>/// <param name="app"></param>/// <returns></returns>public async static Task InitializeApplicationAsync([NotNull] this IApplicationBuilder app){Check.NotNull(app, nameof(app));app.ApplicationServices.GetRequiredService<ObjectAccessor<IApplicationBuilder>>().Value = app;var application = app.ApplicationServices.GetRequiredService<IAbpApplicationWithExternalServiceProvider>();var applicationLifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();//注册程序生命周期停止事件applicationLifetime.ApplicationStopping.Register(() =>{//执行 Abp程序中的 ShutdownAsync 方法, 会调用每个模块中的 ShutdownAsync 方法AsyncHelper.RunSync(() => application.ShutdownAsync());});//释放 Abp 应用applicationLifetime.ApplicationStopped.Register(() =>{application.Dispose();});// 执行 初始化 Abp 方法 ,初始化 每个模块,执行每一个模块中的 InitializeAsync 方法await application.InitializeAsync(app.ApplicationServices);}
  • 初始化每一个模块生命周期方法
   /// <summary>/// 设置服务提供者/// </summary>/// <param name="serviceProvider"></param>protected virtual void SetServiceProvider(IServiceProvider serviceProvider){ServiceProvider = serviceProvider;ServiceProvider.GetRequiredService<ObjectAccessor<IServiceProvider>>().Value = ServiceProvider;}/// <summary>/// 初始化模块/// </summary>/// <returns></returns>protected virtual async Task InitializeModulesAsync(){using (var scope = ServiceProvider.CreateScope()){WriteInitLogs(scope.ServiceProvider);await scope.ServiceProvider.GetRequiredService<IModuleManager>() //初始化模块.InitializeModulesAsync(new ApplicationInitializationContext(scope.ServiceProvider));}}/// <summary>/// 初始化模块/// </summary>/// <param name="context"></param>/// <returns></returns>/// <exception cref="AbpInitializationException"></exception>public virtual async Task InitializeModulesAsync(ApplicationInitializationContext context){// 原先在 AddCoreAbpServices() 方法中 配置的 生命周期,foreach (var contributor in _lifecycleContributors){foreach (var module in _moduleContainer.Modules){try{//执行每个生命周期(加载之前、加载中、加载之后)中的 初始化方法await contributor.InitializeAsync(context, module.Instance);}catch (Exception ex){throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}: {ex.Message}. See the inner exception for details.", ex);}}}_logger.LogInformation("Initialized all ABP modules.");}
  • 四个生命周期 OnApplicationInitializationModuleLifecycleContributor
    OnPreApplicationInitializationModuleLifecycleContributor
    OnPostApplicationInitializationModuleLifecycleContributor
    OnApplicationShutdownModuleLifecycleContributor 中的方法,实际就是调用到具体模块中的生命周期各个方法
public class OnApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase
{public async override Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module){if (module is IOnApplicationInitialization onApplicationInitialization){await onApplicationInitialization.OnApplicationInitializationAsync(context);}}public override void Initialize(ApplicationInitializationContext context, IAbpModule module){(module as IOnApplicationInitialization)?.OnApplicationInitialization(context);}
}public class OnApplicationShutdownModuleLifecycleContributor : ModuleLifecycleContributorBase
{public async override Task ShutdownAsync(ApplicationShutdownContext context, IAbpModule module){if (module is IOnApplicationShutdown onApplicationShutdown){await onApplicationShutdown.OnApplicationShutdownAsync(context);}}public override void Shutdown(ApplicationShutdownContext context, IAbpModule module){(module as IOnApplicationShutdown)?.OnApplicationShutdown(context);}
}public class OnPreApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase
{public async override Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module){if (module is IOnPreApplicationInitialization onPreApplicationInitialization){await onPreApplicationInitialization.OnPreApplicationInitializationAsync(context);}}public override void Initialize(ApplicationInitializationContext context, IAbpModule module){(module as IOnPreApplicationInitialization)?.OnPreApplicationInitialization(context);}
}public class OnPostApplicationInitializationModuleLifecycleContributor : ModuleLifecycleContributorBase
{public async override Task InitializeAsync(ApplicationInitializationContext context, IAbpModule module){if (module is IOnPostApplicationInitialization onPostApplicationInitialization){await onPostApplicationInitialization.OnPostApplicationInitializationAsync(context);}}public override void Initialize(ApplicationInitializationContext context, IAbpModule module){(module as IOnPostApplicationInitialization)?.OnPostApplicationInitialization(context);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/72007.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

IDEA如何查看所有的断点(Breakpoints)并关闭

前言 我们在使用IDEA开发Java应用时,基本上都需要进行打断点的操作,这方便我们排查BUG,也方便我们查看设计的是否正确。不过有时候,我们不希望进入断点,这时候除了点击断点关闭外,有没有更快速的方便关闭所有的断点呢? 如何设置 首先,我们在运行debug模式的时候,切换到…

为什么普通AI不够用?定制AI Agents工具是关键!

1 新建一个实时搜索工具 @tool def web_search(query: str):""" 实时搜索工具 """serp = SerpAPIWrapper()result = serp.run(query)print("实时搜索结果:", result)return result# 初始化工具列表 tools = [web_search]# 创建OpenAI工…

Nginx UI:全新的 Nginx 在线管理平台

前言 Nginx在程序部署中扮演着至关重要的角色,其高性能、高安全性、易于配置和管理的特点,使得它成为现代Web应用部署中不可或缺的一部分。今天大姚给大家分享一款实用的 Nginx Web UI 工具,希望能够帮助到有需要的同学。 工具介绍 Nginx UI一个功能丰富、易于使用的 Nginx …

LSTM神经网络结合PSO粒子群优化对管网优化调度及网络安全入侵检测模型|附数据代码

全文链接:https://tecdat.cn/?p=37878 原文出处:拓端数据部落公众号 分析师:Fan Qiao 在当今科技飞速发展的时代,无论是工业生产中的管网系统,还是信息领域的网络安全,都面临着日益复杂的挑战😕。管网系统作为能源输送和分配的关键基础设施,其优化调度对于提高能源利…

【专题】2024年经销商车后用户研究:洞察车主变化制胜售后未来报告合集PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p=37875 在汽车行业快速变革的时代,“互联网原住民”成为车主群体的重要组成部分。2023 - 2024 年,车后用户线上渠道使用比例不断上升,App/小程序备受青睐,各线上渠道各具优势。同时,购车关注点也在不断变化,价格成为关键因素,本土品牌崛…

线程,进程,协程

线程,进程,协程 进程 是什么: ​ 最小的资源分配单位。 进程上下文切换 ​ 指 cpu 保持原有的进程的状态的同时去切换到下一个进程 ​ 包含两个主要过程:进程地址空间切换和处理器状态切换 线程 是什么 ​ 线程是操作系统中最小的调度单位。 ​ 线程是进程的子集,也称为轻…

Project Euler 638 题解

trivalq-analog,老玩家集体起立!这也就是说: \[\binom{n+m}{n}_q=\sum_{\pi\in L_{n,m}}q^{area(\pi)} \]结束! #include<bits/stdc++.h> using namespace std; #define int long long const int mod=1e9+7,maxn=2e7+5; int qp(int a,int b,int p=mod){int res=1;whi…

【bypass】bash绕过waf 的小技巧

原创 良辰 红队笔记录无意中看到推特上面有个老外分享的一条命令 $0<<<$\\\$(($((1<<1))#10011010))\\$(($((1<<1))#10100011))\其实这个命令就是ls 搜了一下原来是这个意思 $((1<<1)) 将 1 左移 1 位,得到 2。 2#10011010 将二进制数 10011010 转…