我的编程空间,编程开发者的网络收藏夹
学习永远不晚

ABP基础架构深入探索

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

ABP基础架构深入探索

前言

我们将从 ASP.NET Core 的 Startup类开始了解为什么我们需要模块化系统,以及 ABP 如何提供模块化方式来配置和初始化应用程序。然后我们将探索 ASP.NET Core 的依赖注入,以及ABP是如何使用预定义规则(predefined rules)自动进行依赖注入。最后,我们将了解 ASP.NET Core 的配置和选项框架,以及其他类库。

以下是本文的所有主题:

  • 了解模块化
  • 使用依赖注入系统
  • 配置应用程序
  • 实现选项模式
  • 日志系统

一、了解模块化

模块化是一种将大型软件按功能分解为更小的部分,并允许每个部分通过标准化接口进行通信。模块化有以下主要好处:

  • 模块按规则进行隔离后,大大降低了系统复杂性。
  • 模块之间松散耦合,提供了更大的灵活性。因为模块是可组装、可替换的。
  • 因为模块是独立的,所以它允许跨应用被重用。

大多数企业的软件被设计成模块化,但是,实现模块化并不容易。ABP 框架的主要目标之一是为模块化提供基础设施和工具。我们将在后面详细介绍模块化开发,本节只介绍 ABP 模块的基础知识。

Startup 类

在定义ABP的模块之前,建议先熟悉 ASP.NET Core 中的StartUp类,我们看下ASP.NET Core 的Startup类:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddTransient<MyService>();
    }
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

ConfigureServices方法用于配置服务并将新服务注册到依赖注入系统。另一方面,Configure方法用于配置 ASP.NET Core 管道中间件,用于处理 HTTP 请求。在应用程序启动之前,我们需要在Program.cs中配置Startup类:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

这个Startup类是独一无二的,我们只有一个点来配置和初始化所有的服务。但是,在模块化应用程序中,我们希望每个模块都能独立配置和初始化与该模块相关的服务。此外,一个模块通常需要使用或依赖于其他模块,因此模块配置顺序和初始化就非常重要了。我们来看下 ABP 的模块是如何定义的

模块定义

ABP 模块是一组类型(比如类或接口),它们一同开发一同交付的。它是一个程序集(一般来说是Visual Studio 中的一个项目),派生自AbpModule,模块类负责配置和初始化,并在必要时配置依赖模块。

下面是一个短信发送模块的简单定义:

using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace SmsSending
{
    public class SmsSendingModule : AbpModule 
    {
        public override void ConfigureServices(
ServiceConfigurationContext context)
        {
            context.Services.AddTransient<SmsService>();
        }
    }
}

每个模块都可以重写ConfigureServices方法,以便将其服务注册到依赖注入系统。此示例中的SmsService服务被注册为瞬态生命周期。该示例和上面Startup类似。但是,大多时候,您不需要手动注册服务,这要归功ABP 框架的按约定注册系统。

OnApplicationInitialization方法用在服务注册完成后,并且在应用准备就绪后执行。使用此方法,您可以在应用启动时执行任何操作。例如,您可以初始化一个服务:

public class SmsSendingModule : AbpModule 
{
    //...
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var service = context.ServiceProvider.GetRequiredService&lt;SmsService&gt;();
        service.Initialize();
    }
}

这里,我们使用context.ServiceProvider从依赖注入系统请求并初始化服务。可见,此时服务已经完成注册。

您也可以将OnApplicationInitialization方法等同于Startup类的Configure方法。

您可以在此处构建 ASP.NET Core 请求管道。但是,通常我们会在启动模块中配置请求管道,如下一节所述。

模块依赖和启动模块

一个业务应用通常由多个模块组成,ABP 框架允许您声明模块之间的依赖关系。一个应用必须要有一个启动模块。启动模块可以依赖于其他模块,其他模块可以再依赖于其他模块,以此类推。

下图是一个简单的模块依赖关系图:

如果所示,如果模块 A 依赖于模块 B,则模块 B 总是在模块 A 之前初始化。这允许模块 A 使用、设置、更改或覆盖模块 B 定义的配置和服务。

对于示例图,模块初始化的顺序应该是:G、F、E、D、B、C、A。

您不必知道确切的初始化顺序;只需要知道如果你的模块依赖于模块xx,那么模块xx在你的模块之前被初始化。

ABP使用[DependsOn](属性声明)方式来定义模块依赖:

[DependsOn(typeof(ModuleB), typeof(ModuleC))]
public class ModuleA : AbpModule
{    
}

这里,ModuleA通过[DependsOn]依赖于ModuleBModuleC。本例中,启动模块ModuleA负责设置ASP.NET Core 的请求管道:

[DependsOn(typeof(ModuleB), typeof(ModuleC))]
public class ModuleA : AbpModule
{
    //...
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        
        app.UseRouting();
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseEndpoints(endpoints =&gt;
        {
            endpoints.MapControllers();
        });
    }
}

代码块和之前ASP.NET Core的 Startup类 创建请求管道相同。

context.GetApplicationBuilder()context.GetEnvironment()用于从依赖注入中获IApplicationBuilderIWebHostEnvironment服务。

最后,我们在Startup里将ASP.NET Core 和 ABP 框架进行集成:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddApplication&lt;ModuleA&gt;();
    }
    public void Configure(IApplicationBuilder app)
    {
        app.InitializeApplication();
    }
}

services.AddApplication()方法由 ABP 框架定义,用于ABP的模块配置。它按顺序执行了所有模块的ConfigureServices方法。而app.InitializeApplication()方法也是由 ABP 框架定义,它也是按照模块依赖的顺序来执行所有模块的OnApplicationInitialization方法。

ConfigureServicesOnApplicationInitialization方法是模块类中最常用的方法。

模块生命周期

AbpModule中定义的生命周期方法,除了上面看到的ConfigureServices和OnApplicationInitialization,下面罗列其他生命周期相关方法:

PreConfigureServices: 这个方法在ConfigureServices方法之前被调用。它允许您配置服务之前执行的代码。

ConfigureServices:这是配置模块和注册服务的主要方法。

PostConfigureServices: 该方法在ConfigureServices之后调用(包括依赖于您模块的模块),这里可以配置服务后执行的代码。

OnPreApplicationInitialization: 这个方法在OnApplicationInitialization之前被调用。在这个阶段,您可以从依赖注入中解析服务,因为服务已经被初始化。

OnApplicationInitialization:此方法用来配置 ASP.NET Core 请求管道并初始化您的服务。

OnPostApplicationInitialization: 这个方法在初始化阶段后被调用。

OnApplicationShutdown:您可以根据需要自己实现模块的关闭逻辑。带Pre…Post…前缀的方法与原始方法具有相同的目的。它们提供了一种在模块之前或之后执行的一些配置/初始化代码,一般情况下我们很少使用到。

异步生命周期方法

本节介绍的生命周期方法是同步的。在编写本书时,ABP 框架团队正努力在 框架 5.1 版本引入异步生命周期方法。

如前所述,模块类主要包含注册和配置与该模块相关的服务的代码。在下一节中,我们将介绍如何使用 ABP 框架注册服务。

二、使用依赖注入系统

.NET 原生依赖注入

依赖注入是一种获取类的依赖的技术,它将创建类与使用该类分开。

假设我们有一个UserRegistrationService类,它调用SmsService类来发送验证短信,如下:

public class UserRegistrationService
{
    private readonly SmsService _smsService;
    public UserRegistrationService(SmsService smsService)
    {
        _smsService = smsService;
    }
    public async Task RegisterAsync(
        string username,
        string password,
        string phoneNumber)
    {
        //...save user in the database
        await _smsService.SendAsync(
            phoneNumber,
            "Your verification code: 1234"
        );
    }
}

这里的SmsService使用构造函数注入来获取实例。也就是说,依赖注入系统会自动帮我们实例化类的依赖项,并将它们赋值给我们的_smsService。

注意:ABP采用的是ASP.NET Core原生的依赖注入框架,他自己并没有发明依赖注入框架。

在设计服务时,我们还要考虑另外一件重要的事情:服务生命周期。ASP.NET Core 为服务注册提供了三个生命周期选项:

  • Transient(瞬态):每次您请求/注入服务时,都会创建一个新实例。
  • Scoped(范围): 通常这由请求生命周期来评估,您只有在同一范围内才能共享相同的实例。
  • Singleton(单例):在应用内有且仅有一个实例。所有请求都使用相同的实例。该对象在第一次请求创建。以下模块注册了两个服务,一个是瞬态的,另一个是单例的:
public class MyModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.AddTransient&lt;ISmsService, SmsService&gt;();
        context.Services.AddSingleton&lt;OtherService&gt;();
    }
}

context.Services的类型是IServiceCollection,它是一个扩展方法。

在第一个示例中使用接口注册,第二个示例使用引用类注册为单例。

ABP的依赖注入

使用 ABP 框架时,您不必考虑服务注册,这要归功于 ABP 框架独特的服务注册系统。

1.约定式注册

在 ASP.NET Core 中,所有服务需要显式注册到IServiceCollection,如上一节所示。这些注册大多重复,完全可以自动化操作。

ABP 对于以下类型采用自动注册:

  • MVC controllers
  • Razor page models
  • View components
  • Razor components
  • SignalR hubs
  • Application services
  • Domain services
  • Repositories以上类型均使用瞬态生命周期自动注册。如果您还有别的类型,可以考虑接口注册。

2.接口注册

您可以实现以下三种接口来注册:

ITransientDependency

IScopedDependency

ISingletonDependency

例如,在下面代码块中,我们将服务注册为单例:

public class UserPermissionCache : ISingletonDependency
{ }

接口注册很容易并且是推荐的方式,但与下面的属性注册相比,它有一定的局限性。

3.属性注册

属性注册更精细,下面是和属性注册相关的配置参数

Lifetime(enum): 服务的生命周期,包括Singleton,TransientScoped

TryRegister(bool):仅当服务尚未注册时才注册

ReplaceServices(bool):如果服务已经注册,则替换之前的注册

示例代码:

using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace UserManagement
{
    [Dependency(ServiceLifetime.Transient, TryRegister = true)]
    public class UserPermissionCache
    { }
}

4.接口属性混合注册

属性接口一起使用。如果属性定义了属性,属性比接口优先级更高。

如果一个类可能被注入不同的类或接口,具体取决于暴露的类型。

暴露服务

当一个类没有实现接口时,只能通过类引用注入。上一节中的UserPermissionCache类就是通过注入类引用来使用的。

假设我们有一个抽象 SMS 发送的接口:

public interface ISmsService
{
    Task SendAsync(string phoneNumber, string message);
}

假设您要ISmsService实现 Azure 服务:

public class AzureSmsService : ISmsService, ITransientDependency
{
    public async Task SendAsync(string phoneNumber, string message)
    {
        //TODO: ...
    }
}

这里的AzureSmsService实现了ISmsServiceITransientDependency两个接口。而ITransientDependency接口才是用于自动注册到依赖注入中的。这里的注入主要通过命名约定来实现,因为AzureSmsServiceSmsService作为后缀结尾。我们再举一个通过命名约定的例子,假设我们有一个实现多个接口的类:

public class PdfExporter: IExporter, IPdfExporter, ICanExport, ITransientDependency
{ }

PdfExporter服务可以通过注入IPdfExporterIExporter接口来使用,也可以直接注入PdfExporter类引用来使用。但是,您不能使用ICanExport接口注入它,因为名称PdfExporter不以CanExport为后缀。

一旦您使用该ExposeServices属性来暴露服务,如以下代码块所示:

[ExposeServices(typeof(IPdfExporter))]
public class PdfExporter: IExporter, IPdfExporter, ICanExport, ITransientDependency
{ }

现在,您只能通过注入IPdfExporter接口来使用PdfExporter类。

我应该为每个服务定义接口吗?

ABP 不会强迫你这么做,但是通用接口来定义是最佳实践:如果你想松散地耦合你的服务。比如,在单元测试中可以轻松模拟测试数据。

这就是为什么我们将接口与实现物理分离(例如,我们在项目中定义Application.Contracts接口,并在Application项目中实现它们,或者在领域层中定义存储库接口,在基础设施层中实现它们)。

我们已经了解了如何注册和消费服务。另外,某些服务具有选项配置,您需要在使用它们之前对其进行配置。接下来的两节将展开介绍,更多关于ABP基础架构的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

ABP基础架构深入探索

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Electron架构深入探究

这篇文章主要为大家介绍了Electron架构深入探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-09

Android图片框架Glide原理深入探索

对于Glide这个加载图片的框架,很多人都在用,我之前使用的是ImageLoader,最近查资料时,发现Glide才是Google推荐的加载图片框架,功能非常强大,而且还有Google专人维护,要知道,ImageLoader已经没人维护了,除了问题可没人解答。所以有必要整理一下Glide的使用
2022-11-13

深入探讨 Oracle RAC 的架构与特点

Oracle RAC(Real Application Clusters)是Oracle公司提供的一种可扩展性解决方案,允许在多台服务器上运行Oracle数据库实例,将多个服务器组合在一起以提供高可用性和扩展性。Oracle RAC的架构和
深入探讨 Oracle RAC 的架构与特点
2024-03-07

Python带你从浅入深探究Tuple(基础篇)

元组 Python中的元组容器序列(tuple)与列表容器序列(list)具有极大的相似之处,因此也常被称为不可变的列表。 但是两者之间也有很多的差距,元组侧重于数据的展示,而列表侧重于数据的存储与操作。 它们非常相似,虽然都可以存储任意类
2022-06-02

超越 PHP 负载均衡的基础知识:深入探索高级概念

超越 PHP 负载均衡的基础知识,深入了解高级概念,探索优化性能、冗余和可扩展性的方法,以满足您的 Web 应用程序的不断增长的需求。
超越 PHP 负载均衡的基础知识:深入探索高级概念
2024-03-01

Java 语法探险:深入了解编程的基础

语法结构:Java 语法建立在清晰简洁的规则之上,由以下基本结构组成:数据类型:Java 支持基本类型(如 int、float)和引用类型(如 String、Object)。变量:变量用于存储数据值,需要使用数据类型进行声明。运算符:运算符执行数学和逻辑运算。流程控制:条件语句(if、else、switch)和循环(f
Java 语法探险:深入了解编程的基础
2024-04-03

深入探讨Go语言程序的基础知识

《Go语言程序基础知识深入探讨:具体代码示例解析》Go语言作为一种快速、高效的编程语言,越来越受到程序员和开发者的青睐。在学习和掌握Go语言的过程中,深入了解其基础知识是至关重要的。本文将从变量、数据类型、流程控制以及函数等方面展开深入探
深入探讨Go语言程序的基础知识
2024-03-05

HTML语法进阶指南:深入探索网页结构

HTML语法进阶指南:揭开网页结构的奥秘
HTML语法进阶指南:深入探索网页结构
2024-03-09

深入探讨 Golang 架构中的关键组成部分。

Golang(Go语言)作为一种开源的编程语言,在构建现代应用程序时备受关注。其简洁的语法、高效的并发模型和出色的性能,使得Golang在各个领域都有着广泛的应用。在利用Golang构建应用程序的过程中,了解其架构中的关键组成部分是至关重要
深入探讨 Golang 架构中的关键组成部分。
2024-03-02

揭秘 Puppet 的幕后机制:深入探索其工作原理和架构奥秘

Puppet 凭借其出色的基础设施自动化和配置管理能力,成为许多企业构建、管理和更新 IT 基础设施的首选工具。这篇深入探讨了 Puppet 的工作原理和架构。
揭秘 Puppet 的幕后机制:深入探索其工作原理和架构奥秘
2024-02-12

JMX 入门:探索 Java 监控和管理的基础知识

JMX(Java 监控和管理)是一种标准框架,用于监控和管理 Java 应用程序。本文将深入探讨 JMX 的基础知识,指导您使用示例代码实现监控和管理任务。
JMX 入门:探索 Java 监控和管理的基础知识
2024-02-19

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录