ABP源码分析 - 约定注册(3)
入口
//ConfigureServices
foreach (var module in Modules)
{
if (module.Instance is AbpModule abpModule)
{
if (!abpModule.SkipAutoServiceRegistration)
{
// 自动注册服务
Services.AddAssembly(module.Type.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);
}
}
public static IServiceCollection AddAssembly(this IServiceCollection services, Assembly assembly)
{
// 遍历所有的约定注册器,进行服务注册(默认只有一个)
foreach (var registrar in services.GetConventionalRegistrars())
{
registrar.AddAssembly(services, assembly);
}
return services;
}
GetConventionalRegistrars
public static List<IConventionalRegistrar> GetConventionalRegistrars(this IServiceCollection services)
{
return GetOrCreateRegistrarList(services);
}
private static ConventionalRegistrarList GetOrCreateRegistrarList(IServiceCollection services)
{
var conventionalRegistrars = services.GetSingletonInstanceOrNull<IObjectAccessor<ConventionalRegistrarList>>()?.Value;
if (conventionalRegistrars == null)
{
conventionalRegistrars = new ConventionalRegistrarList { new DefaultConventionalRegistrar() }; // 默认约定注册
services.AddObjectAccessor(conventionalRegistrars);
}
return conventionalRegistrars;
}
registrar.AddAssembly(services, assembly) 相当于 DefaultConventionalRegistrar.AddAssembly。所以直接看DefaultConventionalRegistrar的AddAssembly即可。
DefaultConventionalRegistrar继承于ConventionalRegistrarBase,AddAssembly直接调用ConventionalRegistrarBase.AddAssembly。所以先看ConventionalRegistrarBase
public abstract class ConventionalRegistrarBase : IConventionalRegistrar
{
public virtual void AddAssembly(IServiceCollection services, Assembly assembly)
{
// 查找程序集内所有, 是Class,非抽象,非繁星的所有类型。
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);
protected virtual bool IsConventionalRegistrationDisabled(Type type)
{
return type.IsDefined(typeof(DisableConventionalRegistrationAttribute), true);
}
protected virtual void TriggerServiceExposing(IServiceCollection services, Type implementationType, List<Type> serviceTypes)
{
var exposeActions = services.GetExposingActionList();
if (exposeActions.Any())
{
var args = new OnServiceExposingContext(implementationType, serviceTypes);
foreach (var action in exposeActions)
{
action(args);
}
}
}
}
AddType在子类实现,继续看DefaultConventionalRegistrar.AddType
public class DefaultConventionalRegistrar : ConventionalRegistrarBase
{
public override void AddType(IServiceCollection services, Type type)
{
// 明确禁止的,不注入。即类上加[DisableConventionalRegistration]的
if (IsConventionalRegistrationDisabled(type))
{
return;
}
// 获取[Dependency]属性
var dependencyAttribute = GetDependencyAttributeOrNull(type);
// 先从Dependency获取,没找到,则从ITransientDependency,ISingletonDependency,IScopedDependency获取
var lifeTime = GetLifeTimeOrNull(type, dependencyAttribute);
if (lifeTime == null)
{
return;
}
// 获取暴露的服务
var exposedServiceTypes = ExposedServiceExplorer.GetExposedServices(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);
}
}
}
protected virtual ServiceDescriptor CreateServiceDescriptor(
Type implementationType,
Type exposingServiceType,
List<Type> allExposingServiceTypes,
ServiceLifetime lifeTime)
{
if (lifeTime.IsIn(ServiceLifetime.Singleton, ServiceLifetime.Scoped))
{
var redirectedType = GetRedirectedTypeOrNull(
implementationType,
exposingServiceType,
allExposingServiceTypes
);
if (redirectedType != null)
{
return ServiceDescriptor.Describe(
exposingServiceType,
provider => provider.GetService(redirectedType),
lifeTime
);
}
}
return ServiceDescriptor.Describe(
exposingServiceType,
implementationType,
lifeTime
);
}
protected virtual Type GetRedirectedTypeOrNull(
Type implementationType,
Type exposingServiceType,
List<Type> allExposingServiceTypes)
{
if (allExposingServiceTypes.Count < 2)
{
return null;
}
if (exposingServiceType == implementationType)
{
return null;
}
if (allExposingServiceTypes.Contains(implementationType))
{
return implementationType;
}
return allExposingServiceTypes.FirstOrDefault(
t => t != exposingServiceType && exposingServiceType.IsAssignableFrom(t)
);
}
protected virtual DependencyAttribute GetDependencyAttributeOrNull(Type type)
{
return type.GetCustomAttribute<DependencyAttribute>(true);
}
protected virtual ServiceLifetime? GetLifeTimeOrNull(Type type, [CanBeNull] DependencyAttribute dependencyAttribute)
{
return dependencyAttribute?.Lifetime ?? GetServiceLifetimeFromClassHierarcy(type);
}
protected virtual ServiceLifetime? GetServiceLifetimeFromClassHierarcy(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;
}
}
服务暴露
ExposedServiceExplorer
public static class ExposedServiceExplorer
{
// 默认所有以大写I开头的接口及自身
private static readonly ExposeServicesAttribute DefaultExposeServicesAttribute =
new ExposeServicesAttribute
{
IncludeDefaults = true,
IncludeSelf = true
};
public static List<Type> GetExposedServices(Type type)
{
return type
.GetCustomAttributes(true)
.OfType<IExposedServiceTypesProvider>()
.DefaultIfEmpty(DefaultExposeServicesAttribute)
.SelectMany(p => p.GetExposedServiceTypes(type))
.Distinct()
.ToList();
}
}
ExposeServicesAttribute继承IExposedServiceTypesProvider
public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider
{
public Type[] ServiceTypes { get; }
public bool IncludeDefaults { get; set; }
public bool IncludeSelf { get; set; }
public ExposeServicesAttribute(params Type[] serviceTypes)
{
ServiceTypes = serviceTypes ?? new Type[0];
}
public Type[] GetExposedServiceTypes(Type targetType)
{
var serviceList = ServiceTypes.ToList();
if (IncludeDefaults) // 默认所有以大写I开头的接口以及自身(如果IncludeSelf=true)
{
foreach (var type in GetDefaultServices(targetType))
{
serviceList.AddIfNotContains(type);
}
if (IncludeSelf)
{
serviceList.AddIfNotContains(targetType);
}
}
else if (IncludeSelf)
{
serviceList.AddIfNotContains(targetType); // 仅自身
}
return serviceList.ToArray();
}
// 以大写I开头的接口作为服务
private static List<Type> GetDefaultServices(Type type)
{
var serviceTypes = new List<Type>();
foreach (var interfaceType in type.GetTypeInfo().GetInterfaces())
{
var interfaceName = interfaceType.Name;
if (interfaceName.StartsWith("I"))
{
interfaceName = interfaceName.Right(interfaceName.Length - 1);
}
if (type.Name.EndsWith(interfaceName))
{
serviceTypes.Add(interfaceType);
}
}
return serviceTypes;
}
}
ABP源码分析 - 约定注册(3)的更多相关文章
- ABP源码分析 - 约定注册(2)
比较随意,记录下过程,以便忘了以后重拾. 所谓约定注册是指不需要明确写代码注入,只需要按约定规则写服务类,框架自动完成服务注册. 例如,只要这样写,框架就会自动注册. public class Tax ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...
- ABP源码分析六:依赖注入的实现
ABP的依赖注入的实现有一个本质两个途径:1.本质上是依赖于Castle这个老牌依赖注入的框架.2.一种实现途径是通过实现IConventionalDependencyRegistrar的实例定义注入 ...
- [Abp 源码分析]三、依赖注入
0.简要介绍 在 Abp 框架里面,无时无刻不存在依赖注入,关于依赖注入的作用与好处我就不在这里多加赘述了,网上有很多解释的教程.在 [Abp 源码分析]一.Abp 框架启动流程分析 里面已经说过,A ...
- [Abp 源码分析]十七、ASP.NET Core 集成
0. 简介 整个 Abp 框架最为核心的除了 Abp 库之外,其次就是 Abp.AspNetCore 库了.虽然 Abp 本身是可以用于控制台程序的,不过那样的话 Abp 就基本没什么用,还是需要集合 ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析四:Configuration
核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...
随机推荐
- python 程序小练习
print("Type integers,each followed by Enter; or just Enter to finish") total = 0 count = 0 ...
- 分布式锁用Redis与Zookeeper的使用
为什么用分布式锁? 在讨论这个问题之前,我们先来看一个业务场景: 系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会 ...
- 4.监控Redis--单节点
prometheus监控redis需要用到redis_exporter. redis_exporter 项目地址:https://github.com/oliver006/redis_exporter ...
- Linux巡检检查项
不定时更新...... 1)服务器 1.1 SELINUX检查(sestatus) 1.2 资源限制检查(ulimit -a) 1.3 最近登录(last) 1.4 操作系统版本(cat /etc/r ...
- 09自动售货机综设实验(含按键消抖,led和状态机)
一设计功能 1.上次状态机的练习 2这次自动售货机综设 (一)对比两次的售货机 上次售货机的关键是画出状态转移图.明确输入分几种,输出是啥,有哪些状态.如下图所示 (二)系统或综合设计的经验: 既然这 ...
- 在Unity中用UGUI制作可输入下拉框
Unity中UGUI制作可输入下拉框 目录 Unity中UGUI制作可输入下拉框 前言 组件分析 制作流程 总结 前言 在搜索引擎以及一些网页中我们常常可以看见这样一种UI控件,看上去是一个输入框,在 ...
- Citus 分布式 PostgreSQL 集群 - SQL Reference(创建和修改分布式表 DDL)
创建和分布表 要创建分布式表,您需要首先定义表 schema. 为此,您可以使用 CREATE TABLE 语句定义一个表,就像使用常规 PostgreSQL 表一样. CREATE TABLE ht ...
- 什么是GUI?
图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面.图形用户界面是一种人与计算机通信的界面显示格式,允许用户使用鼠 ...
- 创建自定义ClassLoader,绕过双亲委派
1.什么是类加载 通过javac将.java文件编译成.class字节码文件后,则需要将.class加载到JVM中运行,哪么是谁将.class加载到JVM的呢?那就是类加载器啦. 2.类加载器类型 B ...
- ZooKeeper 面试题?
ZooKeeper 是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群 中各个节点的状态根据节点提交的反馈进行下一步合理操作.最终,将简单易用 的接口和性能高效.功能稳定的系统提供给用户. ...