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对象和扩展方法很巧妙的实现了配 ...
随机推荐
- windows禁止iis开机自启
按Win 和R,打开运行窗口.输入services.msc确定打开服务窗口.找到World Wide Web Publishing Service禁用并停止.
- Laravel 8 图片上传七牛云
1.利用 composer 下载依赖包 composer require itbdw/laravel-storage-qiniu 2.打开 config 文件夹下的 app.php 文件,在 prov ...
- spring boot使用注解进行模糊查询
spring boot中mybatis使用注解进行模糊查询@Select("select * from dept where dname like CONCAT('%',#{dname},' ...
- Java 代码注意细节
代码优化的目标是: 1.减小代码的体积 2.提高代码运行的效率 代码优化细节 1.尽量指定类.方法的final修饰符 带有final修饰符的类是不可派生的.在Java核心API中,有许多应用final ...
- 嵌入式linux驱动开发 笔记
@ 目录 首个驱动hellodrv 1.编写源码 2.编译模块 3.加载驱动 首个驱动hellodrv 3.如果下载不到,就自己编写,并编译驱动. 1.编写源码 2.编译模块 1.先写makefile ...
- 玩转SpringBoot之定时任务@Scheduled线程池配置
序言 对于定时任务,在SpringBoot中只需要使用@Scheduled 这个注解就能够满足需求,它的出现也给我们带了很大的方便,我们只要加上该注解,并且根据需求设置好就可以使用定时任务了. 但是, ...
- linux下的shell脚本
先说明以下内容来自: http://c.biancheng.net/cpp/shell/ ,C语言中文网,请大家支持原作,点击链接查看. 我写下来只是作为笔记,如果侵权,请留言,立马删除. Shell ...
- Kafka学习(一)
作者:普适极客链接:https://www.zhihu.com/question/53331259/answer/1321992772来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
- 【Java面试宝典】你们线上应用的 JVM 参数有哪些?
-server-Xms6000M-Xmx6000M-Xmn500M-XX:PermSize=500M-XX:MaxPermSize=500M-XX:SurvivorRatio=65536-XX:Max ...
- XML的解析方式有哪几种?有什么区别?
有DOM.SAX等. DOM:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种标准方式. DOM中的核心概念就是节点.DOM在分析XML文档 ...