ABP官网:https://www.abp.io/
ABP GitHub:https://github.com/abpframework/abp
要学习ABP,第一手资料肯定是官方文档,详细信息也请查看官方文档。
基本概念
ABP vNext(以下简称ABP)的前身是asp.net boilerplate(老版abp),它不是一个简单的版本更新,而是完全基于.NET Core的重写,废话不多说,来到这里你至少是知道ABP的。
ABP是一个开源应用程序框架,专注于基于ASP.NET Core的Web应用程序开发,但也支持开发其他类型的应用程序。
计划从基础搭建开始,逐步把涉及到的东西都走一遍,这将会是一个持续更新的系列文章。
本篇核心:
- 模块化(Modularity)
- 依赖注入(Dependency Injection)
使用ABP开发新项目的最简单方法是使用启动模板,但我不打算这么干,我更想知道abp是怎么启动的,我们不仅要知其然,更要知其所以然。
那么我们从一个空项目开始,手动安装abp框架。
ABP开发.Net Core Console应用程序
1、使用Visual Studio创建一个新的.Net Core Console应用程序,就叫MyFirstAbpConsole。
2、添加引用Volo.Abp.Core
下面我们就引入abp的第一个重要的概念,模块化(Modularity),abp是一个模块化的框架,一个abp框架至少要包含一个模块(从AbpModule类派生的模块类),即启动模块或者叫根模块。
3、创建一个模块类MyFirstAbpConsoleAppModule,并继承自AbpModule,这样一个模块就有了,暂时我们什么也不做,如下所示:
4、回到Program类的Main方法中,我们来启动abp。
abp框架中提供了一个应用程序工厂类,用于创建启动模块,代码如下:
这样我们就创建并启动了我们的模块应用并且完成了初始化。
4、新建一个HelloWorldService的服务类,里面定义了一个SayHello的方法,代码如下:
那么我们怎么调用我们的这个服务呢,这里又引入了一个重要的概念,依赖注入(Dependency Injection)。
在abp中,服务都会被注册到容器中,在需要用到时注入该服务。
首先,把服务注册到容器中,我们暂时先说一种方式,即继承自ITransientDependency接口(瞬态的,不同生命周期后面再说),框架会自动把服务注册到容器中
代码如下:
5、获取服务并调用服务输出Hello World
回到Program类的Main方法,前面我们应用已经创建并初始化完成,接着我们调用服务如下:
这里我们使用了IServiceProvider接口来获取服务,在abp中注入服务的方式有多种,后面再说。
6、运行程序,结果输出Hello World 说明整个程序框架运行成功。
总结:
- abp是一个基于模块化的框架,启动并运行abp框架,至少要包含一个启动模块
- abp中的服务都是通过注入的方式调用,需要某个服务时不再需要new
- abp中的自动注册服务的接口有三个,区别是生命周期不同,分别是ITransientDependency、IScopedDependency和ISingletonDependency
下面我们来改造下上面的依赖注入,我们来使用Autofac手动注册服务
第一步,添加Volo.Abp.Autofac引用
第二步,回到MyFirstAbpConsoleAppModule模块类,添加依赖项,把Autofac模块添加到当前模块中来(abp是基于模块化的,所以Autofac对于abp来说也是一个模块)
第三步,原先的 HelloWorldService继承的接口ITransientDependency将不再需要,我们来使用Autofac手动把HelloWorldService注入到当前模块中,需要我们来重写AbpModule中的ConfigureServices虚方法,代码如下:
第四步,创建模块的代码也要修改下,增加一个options操作,使用Autofac,如下:
第五步,我们再次运行程序,结果输出Hello World 没有问题。
至此,我们知道了如何创建一个abp的模块、初始化应用、创建服务、注册服务以及如何注入服务的一个最简单流程。
本次内容的重点是理解模块化、依赖注入两个知识点,这两点真的很重要,我们再展开聊聊
第一点:abp把应用进行了模块化,每个模块里定义自己的服务,并在它自己的独立模块类中通过依赖注入进行注册。
public class SomeModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { //在这里注册依赖项 } }
比如上面我们使用Autofac框架时,Autofac本身自己就是一个独立模块,然后我们使用它,就引入它,并且注册到我们的模块中,就是这么个意思
abp框架很庞大,但是不臃肿,因为我们按需分配,我们只引入并注册的是我们需要的。
第二点:abp的依赖注入系统,是基于微软的 dependency injection extension 库(Microsoft.Extensions.DependencyInjection nuget package)开发的,因此,它的文档在abp中也是有效的。
abp框架提供了多种注册方式,主要是两大类,一种是按约定的自动注册,一种是手动注册。这里简单讲一下,详细可以看官方文档。
常规注册
abp引入了常规服务注册,你不需要按照约定去做任何事情去注册一个服务,它是自定完成的。如果你想禁用它,你可以通过重写PreConfigureServices方法设置SkipAutoServiceRegistration = true即可。一旦跳过自动注册,你就应该手动的注册你的所有的服务,在这种情况下,扩展方法AddAssemblyOf能够帮助你按照约定注册你的所有服务,看个例子:
public class AppModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { SkipAutoServiceRegistration = true; } public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddAssemblyOf<AppModule>(); } }
固有的注册类型
默认情况下,某些特定的类型已经注册到依赖注入中
比如,上面的模块类就是作为单例(singleton)注册的,无需我们手动处理。
依赖接口注册
如果你实现了这些接口,你的类就会自动的注册到依赖注入中。比如上面我们首先使用的方式就是这种方式。
- ITransientDependency:注册为瞬态生命周期,每次调用都是一个新的实例
- IScopedDependency:注册为一个范围内的生命周期,同一个http请求的范围内使用的同一个实例
- ISingletonDependency:注册为单例模式,首次请求会创建这个服务,后续的所有请求都使用相同的实例,存在于整个应用程序的生命周期中。
依赖特性注册
是使用属性DependencyAttribute,例子:
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] public class SomeService { …… }
手动注册
也可以通过手动方式注册你需要的服务,上面已经用到了,就是在模块类中的重写配置服务的方法中手动添加,这种其实是微软的标准的注入方式。
注入依赖项
如何调用已注册的服务呢,abp提供了三种常见的方式
- 构造函数注入,这是将服务注入类的最常见方法
- 属性注入,这点微软是不支持的,abp可以与第三方DI提供程序(Autofac)集成,从而可以实现属性注入
- 从IServiceProvider解析服务,即通过注入IServerProvider到类中,然后通过它来获取服务,使用的很少
关于更多的如何注册服务到容器以及将容器中的服务注入类的问题,请查看abp官方文档和微软的依赖注入的文档吧。
通过上面的内容,我们已经通过一个控制台项目了解到如何运行abp框架,以及它的一些基本概念,现在,我们将基于Asp.Net Core Web 应用,继续了解下abp框架如何在Web项目中工作。
ABP创建Asp.Net Core Web MVC应用程序
1、创建一个Asp.Net Core Web 应用程序MyFirstAbpWeb,使用空模板
2、添加引用:Volo.Abp.AspNetCore.Mvc
3、新建一个MyFirstAbpWebModule模块类,因为是.NetCore的 MVC项目,所有添加一个依赖。
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; namespace MyFirstAbpWeb { [DependsOn(typeof(AbpAspNetCoreMvcModule))] public class MyFirstAbpWebModule : AbpModule { } }
4、配置启动应用程序和初始化应用程序
4.1、打开Startup启动类,在ConfigureServices方法中增加一段代码,把刚刚新建的模块类注册进来(创建启动模块)。
public void ConfigureServices(IServiceCollection services) { services.AddApplication<MyFirstAbpWebModule>(options => { }); }
4.2、配置应用程序初始化,继续修改Startup启动类,在Configure方法中增加一行代码,并且把原来的代码剪切出来
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.InitializeApplication(); }
回到模块类中,重写OnApplicationInitialization方法,把剪切的内容粘贴过来,并稍作修改
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Hosting; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; namespace MyFirstAbpWeb { [DependsOn(typeof(AbpAspNetCoreMvcModule))] public class MyFirstAbpWebModule : AbpModule { public override void OnApplicationInitialization(ApplicationInitializationContext context) { var env = context.GetEnvironment(); var app = context.GetApplicationBuilder(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); //app.UseEndpoints(endpoints => //{ // endpoints.MapGet("/", async context => // { // await context.Response.WriteAsync("Hello World!"); // }); //}); app.UseConfiguredEndpoints(); } } }
5、配置完成后,新建一个控制器文件夹Controllers,在里面新建一个HomeController,继承自AbpController
using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; namespace MyFirstAbpWeb.Controllers { public class HomeController : AbpController { public ActionResult Index() { return Content("Hello World"); } } }
运行后,浏览器输出Hello World
至此,我们使用了一个最基本的abp框架 ,后面我们将进入一个实战阶段