Abp vNext 依赖注入
介绍
ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的。所以我们采用dotnet自带的注入方式也是支持的。
- 由于ABP是一个模块化框架,因此每个模块都定义它自己的服务并在它自己的单独模块类中通过依赖注入进行注册.例:
public class BlogModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//在此处注入依赖项
// dotnet自带依赖注入方式也是支持的
context.services.AddTransient
context.services.AddScoped
context.services.AddSingleton
}
}
Autofac
Autofac 是.Net世界中最常用的依赖注入框架之一. 相比.Net Core标准的依赖注入库, 它提供了更多高级特性, 比如动态代理和属性注入.
集成
1.安装 Volo.Abp.Autofac nuget 包到你的项目 (对于一个多项目应用程序, 建议安装到可执行项目或者Web项目中.)
2.模块添加 AbpAutofacModule 依赖:
[DependsOn(typeof(AbpAutofacModule))]
public class MyModule : AbpModule
{
//...
}
}
3.配置 AbpApplicationCreationOptions 用 Autofac 替换默认的依赖注入服务. 根据应用程序类型, 情况有所不同
- ASP.NET Core 应用程序
public class Program
{
public static int Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseAutofac(); //Integrate Autofac!
}
- 控制台应用程序
namespace AbpConsoleDemo
{
class Program
{
static void Main(string[] args)
{
using (var application = AbpApplicationFactory.Create<AppModule>(options =>
{
options.UseAutofac(); //Autofac integration
}))
{
//...
}
}
}
}
依照约定的注册
如果实现这些接口,则会自动将类注册到依赖注入:
- ITransientDependency 注册为transient生命周期.
- ISingletonDependency 注册为singleton生命周期.
- IScopedDependency 注册为scoped生命周期.
默认特定类型
一些特定类型会默认注册到依赖注入.例子:
- 模块类注册为singleton.
- MVC控制器(继承Controller或AbpController)被注册为transient.
- MVC页面模型(继承PageModel或AbpPageModel)被注册为transient.
- MVC视图组件(继承ViewComponent或AbpViewComponent)被注册为transient.
- 应用程序服务(实现IApplicationService接口或继承ApplicationService类)注册为transient.
- 存储库(实现IRepository接口)注册为transient.
- 域服务(实现IDomainService接口)注册为transient.
手动注册
在某些情况下,你可能需要向IServiceCollection手动注册服务,尤其是在需要使用自定义工厂方法或singleton实例时.在这种情况下,你可以像Microsoft文档描述的那样直接添加服务.
public class BlogModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.services.AddTransient<ITestMicrosoftManager, TestMicrosoftManager>();
}
}
如何使用
构造函数注入
- 构造方法注入是将依赖项注入类的首选方式
属性注入
- Microsoft依赖注入库不支持属性注入.但是,ABP可以与第三方DI提供商(例如Autofac)集成,以实现属性注入。
- 属性注入依赖项通常被视为可选依赖项.这意味着没有它们,服务也可以正常工作.Logger就是这样的依赖项,MyService可以继续工作而无需日志记录.
public class MyService : ITransientDependency
{
public ILogger<MyService> Logger { get; set; }
public MyService()
{
Logger = NullLogger<MyService>.Instance;
}
public void DoSomething()
{
//...使用 Logger 写日志...
}
}
IServiceProvider
直接从IServiceProvider解析服务.在这种情况下,你可以将IServiceProvider注入到你的类并使用
public class MyService : ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public MyService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoSomething()
{
var taxCalculator = _serviceProvider.GetService<ITaxCalculator>();
//...
}
}
服务替换
在某些情况下,需要替换某些接口的实现.
- ITestManager有一个默认实现DefaultManager,但是我现在想替换成TestReplaceManager,该如何操作呢?
原生dotnet方式替换
services.Replace(ServiceDescriptor.Transient<ITestManager, TestReplaceManager>());
Abp支持
- 加上Dependency特性标签
- 加上ExposeServices特性标签
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ITestManager))]
public class TestReplaceManager : ITestManager, ITransientDependency
{
public void Print()
{
Console.WriteLine("TestReplaceManager");
}
}
问题
- 有时候我们实现了ITransientDependency,ISingletonDependency,IScopedDependency但是再运行是还是提示依赖注入失败?
- 实现类的名称拼写错误
- 比如接口名称为ITestAppService,但是实现类为DefaultTessAppService,这个时候编译不会报错,但是运行报错,下面会基于源码分析。
public class DefaultTessAppService : ApplicationService, ITestAppService
{
// ....
}
- 我通过[Dependency(ReplaceServices = true)]替换服务没有生效?
- 请添加[ExposeServices(typeof(ITestManager))]显示暴露服务,下面会基于源码分析。
源码分析
- 进入到Startup.AddApplication源码
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<xxxManagementHttpApiHostModule>();
}
}
- 进入到await app.ConfigureServicesAsync()源码
public async static Task<IAbpApplicationWithExternalServiceProvider> CreateAsync(
[NotNull] Type startupModuleType,
[NotNull] IServiceCollection services,
Action<AbpApplicationCreationOptions>? optionsAction = null)
{
var app = new AbpApplicationWithExternalServiceProvider(startupModuleType, services, options =>
{
options.SkipConfigureServices = true;
optionsAction?.Invoke(options);
});
await app.ConfigureServicesAsync();
return app;
}
- 主要查看ConfigureServices下的Services.AddAssembly(assembly)方法。
public virtual async Task ConfigureServicesAsync()
{
// 省略...
var assemblies = new HashSet<Assembly>();
//ConfigureServices
foreach (var module in Modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
var assembly = module.Type.Assembly;
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);
}
}
// 省略...
}
4.进入下面AddAssembly下AddType的逻辑
public class DefaultConventionalRegistrar : ConventionalRegistrarBase
{
public override void AddType(IServiceCollection services, Type type)
{
if (IsConventionalRegistrationDisabled(type))
{
return;
}
// 查看是否有DependencyAttribute特性标签
var dependencyAttribute = GetDependencyAttributeOrNull(type);
// 判断是否有实现接口,注入对于的类型。
var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);
if (lifeTime == null)
{
return;
}
var exposedServiceTypes = GetExposedServiceTypes(type);
TriggerServiceExposing(services, type, exposedServiceTypes);
foreach (var exposedServiceType in exposedServiceTypes)
{
var serviceDescriptor = CreateServiceDescriptor(
type,
exposedServiceType,
exposedServiceTypes,
lifeTime.Value
);
if (dependencyAttribute?.ReplaceServices == true)
{
services.Replace(serviceDescriptor);
}
else if (dependencyAttribute?.TryRegister == true)
{
services.TryAdd(serviceDescriptor);
}
else
{
services.Add(serviceDescriptor);
}
}
}
}
// GetLifeTimeOrNull
protected virtual ServiceLifetime? GetLifeTimeOrNull(Type type, DependencyAttribute? dependencyAttribute)
{
return dependencyAttribute?.Lifetime ?? GetServiceLifetimeFromClassHierarchy(type) ?? GetDefaultLifeTimeOrNull(type);
}
// abp 三个生命周期
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;
}
5.重点到了,看下为什么名称错误为什么导致注入失败。
- 通过接口的名称去获取实现。
- 也能解释有时候不显示指定ExposeServices可能替换失败的问题
public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider
{
// 省略...
private static List<Type> GetDefaultServices(Type type)
{
var serviceTypes = new List<Type>();
foreach (var interfaceType in type.GetTypeInfo().GetInterfaces())
{
var interfaceName = interfaceType.Name;
if (interfaceType.IsGenericType)
{
interfaceName = interfaceType.Name.Left(interfaceType.Name.IndexOf('`'));
}
// 查询到实现类的名称是否是移除I
if (interfaceName.StartsWith("I"))
{
interfaceName = interfaceName.Right(interfaceName.Length - 1);
}
// 查询到实现类的名称是否以接口名结尾
if (type.Name.EndsWith(interfaceName))
{
serviceTypes.Add(interfaceType);
}
}
return serviceTypes;
}
}
Abp vNext Pro
- Abp Vnext Pro Github地址 的 Vue3 实现版本 开箱即用的中后台前端/设计解决方案.
- 文档地址
- 演示地址:用户名admin 密码1q2w3E*
- Abp Vnext Pro Suite Github地址 代码生成器。
- 演示地址:用户名admin 密码1q2w3E*
- 视频教程
如果觉得可以,不要吝啬你的小星星哦
Abp vNext 依赖注入的更多相关文章
- ABP的依赖注入
目录 说说ABP的依赖注入 代码追踪 说说ABP的依赖注入 上篇abp运行机制分析分析了ABP在启动时,都做了那些事:这篇我们来说说ABP的最核心的一部分:依赖注入(DependencyInjecti ...
- ABP vNext 自动注入,暗藏天坑如斯
导言 我们在使用ABP vNext框架时,都知道该框架为我们实现了自动依赖注入(实现自动注入需要在项目里面创建Module类,并且将Module类上的DependsOn到相应的启动Module类或调用 ...
- ABP框架 - 依赖注入
文档目录 本节内容: 什么是依赖注入 传统方式的问题 解决方案 构造器注入模式 属性注入模式 依赖注入框架 ABP 依赖注入基础 注册依赖 约定注入 辅助接口 自定义/直接 注册 使用IocManag ...
- ABP之依赖注入
写在开头 ABP开源项目最近有点小火,还开展了线下活动.本着学习DDD的心态与学习开源代码的的好奇,我也看了一遍ABP源码,在此将自己学习ABP的一些心得记录下来. 作为核心的IoC 作为一种解耦的方 ...
- (译)ABP之依赖注入
原文地址:https://aspnetboilerplate.com/Pages/Documents/Dependency-Injection 什么是依赖注入 传统方式的问题 解决方案 构造函数注入 ...
- Asp.Net Core 3.1 Api 集成Abp项目依赖注入
Abp 框架 地址https://aspnetboilerplate.com/ 我们下面来看如何在自己的项目中集成abp的功能 我们新建core 3.1 API项目和一个core类库 然后 两个项目都 ...
- 3. abp依赖注入的分析.md
abp依赖注入的原理剖析 请先移步参考 [Abp vNext 源码分析] - 3. 依赖注入与拦截器 本文此篇文章的补充和完善. abp的依赖注入最后是通过IConventionalRegister接 ...
- ABP vNext系列文章03---依赖注入
一.依赖注入的类型注册 ABP的依赖注入系统是基于Microsoft的依赖注入扩展库(Microsoft.Extensions.DependencyInjection nuget包)开发的.因此,它的 ...
- ABP(现代ASP.NET样板开发框架)系列之6、ABP依赖注入
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之6.ABP依赖注入 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...
- ABP源码分析六:依赖注入的实现
ABP的依赖注入的实现有一个本质两个途径:1.本质上是依赖于Castle这个老牌依赖注入的框架.2.一种实现途径是通过实现IConventionalDependencyRegistrar的实例定义注入 ...
随机推荐
- 18.详解AQS家族的成员:Semaphore
关注:王有志,一个分享硬核Java技术的互金摸鱼侠. 欢迎你加入Java人的提桶跑路群:共同富裕的Java人 今天我们来聊一聊AQS家族中另一个重要成员Semaphore,我只收集到了一道关于Sema ...
- Linux系统基础知识与自学方法
Linux系统基础知识与自学方法 大部分非计算机相关的朋友也经常使用电脑,所以我们频繁接触的是Windows系统.关于这个系统的评价不一,一部分人觉得简洁快捷,一部分人觉得问题(病毒.弹窗)多多,总之 ...
- HiveSQL在使用聚合类函数的时候性能分析和优化详解
概述 前文我们写过简单SQL的性能分析和解读,简单SQL被归类为select-from-where型SQL语句,其主要特点是只有map阶段的数据处理,相当于直接从hive中取数出来,不需要经过行变化. ...
- 如何取消Blazor Server烦人的重新连接?
如何取消Blazor Server烦人的重新连接? 相信很多Blazor的用户在开发内部系统上基本上都选择速度更快,加载更快的Blazor Server模式. 但是Blazor Server由于是Si ...
- 前端vue uni-app多图片上传组件,支持单个文件,多个文件上传 步骤条step使用
快速实现多图片上传组件,支持单个文件,多个文件上传 步骤条step使用; 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=1274 ...
- 9. SpringMVC处理ajax请求
9.1.@RequestBody @RequestBody 可以获取请求体信息,使用@RequestBody 注解标识控制器方法的形参,当前请求的请求体就会为当前注解所标识的形参赋值 <!--此 ...
- Dlang 与 C 语言交互(二)
Dlang 与 C 语言交互(二) 随着需求不断增加,发现好像需要更多的东西了.在官网上找不到资料,四处拼凑才有了本文的分享. 上一文(DLang 与 C 语言交互(一) - jeefy - 博客园) ...
- 自然语言处理 Paddle NLP - 检索式文本问答-理论
问答系统(Question Answering System,QA) 是信息检索系统的一种高级形式,它能用准确.简洁的自然语言回答用户用自然语言提出的问题.其研究兴起的主要原因是人们对快速.准确地获取 ...
- JVM GC配置指南
本文旨在简明扼要说明各回收器调优参数,如有疏漏欢迎指正. 1.JDK版本 以下所有优化全部基于JDK8版本,强烈建议低版本升级到JDK8,并尽可能使用update_191以后版本. 2.如何选择垃圾回 ...
- Linux 日志服务管理
日志管理 1 系统日志管理 1 rsyslog系统日志服务 日志记录的内容包括: 历史事件:时间,地点,人物,事件 Jul 18 14:30:53 # 时间 ubuntu2204 # 地点 (在哪个主 ...