網站首頁 編程語言 正文
前言
我們將從 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<SmsService>(); ????????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]
依賴于ModuleB
和ModuleC
。本例中,啟動模塊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 => ????????{ ????????????endpoints.MapControllers(); ????????}); ????} }
代碼塊和之前ASP.NET Core的?Startup類?創建請求管道相同。
context.GetApplicationBuilder()
和context.GetEnvironment()
用于從依賴注入中獲IApplicationBuilder
和IWebHostEnvironment
服務。
最后,我們在Startup
里將ASP.NET Core 和 ABP 框架進行集成:
public class Startup { ????public void ConfigureServices(IServiceCollection?services) ????{ ????????services.AddApplication<ModuleA>(); ????} ????public void Configure(IApplicationBuilder app) ????{ ????????app.InitializeApplication(); ????} }
services.AddApplication()
方法由 ABP 框架定義,用于ABP的模塊配置。它按順序執行了所有模塊的ConfigureServices
方法。而app.InitializeApplication()
方法也是由 ABP 框架定義,它也是按照模塊依賴的順序來執行所有模塊的OnApplicationInitialization
方法。
ConfigureServices
和OnApplicationInitialization
方法是模塊類中最常用的方法。
模塊生命周期
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<ISmsService,?SmsService>(); ????????context.Services.AddSingleton<OtherService>(); ????} }
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
,Transient
和Scoped
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
實現了ISmsService
和ITransientDependency
兩個接口。而ITransientDependency
接口才是用于自動注冊到依賴注入中的。這里的注入主要通過命名約定來實現,因為AzureSmsService
以SmsService
作為后綴結尾。我們再舉一個通過命名約定的例子,假設我們有一個實現多個接口的類:
public class PdfExporter: IExporter, IPdfExporter, ICanExport, ITransientDependency { }
PdfExporter
服務可以通過注入IPdfExporter
和IExporter
接口來使用,也可以直接注入PdfExporter
類引用來使用。但是,您不能使用ICanExport
接口注入它,因為名稱PdfExporter
不以CanExport
為后綴。
一旦您使用該ExposeServices
屬性來暴露服務,如以下代碼塊所示:
[ExposeServices(typeof(IPdfExporter))] public class PdfExporter: IExporter, IPdfExporter, ICanExport, ITransientDependency { }
現在,您只能通過注入IPdfExporter
接口來使用PdfExporter
類。
我應該為每個服務定義接口嗎?
ABP 不會強迫你這么做,但是通用接口來定義是最佳實踐:如果你想松散地耦合你的服務。比如,在單元測試中可以輕松模擬測試數據。
這就是為什么我們將接口與實現物理分離(例如,我們在項目中定義Application.Contracts
接口,并在Application
項目中實現它們,或者在領域層中定義存儲庫接口,在基礎設施層中實現它們)。
原文鏈接:https://www.cnblogs.com/jackyfei/p/16272230.html
相關推薦
- 2023-10-15 前端console.log打印內容與后端請求返回數據不一致
- 2022-07-15 教你docker方式部署nacos_docker
- 2022-07-28 使用正則表達式從鏈接中獲取圖片名稱_正則表達式
- 2023-07-22 SpringBoot操作MongoDB時,對同一個字段設置多次條件
- 2023-10-30 springboot 配置文件加載順序
- 2023-01-09 Spark處理trick總結分析_相關技巧
- 2022-05-04 基于Python中的turtle繪畫星星和星空_python
- 2022-07-10 promise封裝ajax請求
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支