ABP Framework-Feature源码解析
目录
https://abp.io/docs/6.0/Features
Version
6.0.3
Package
Volo.Abp.Features
//独立模块
Volo.Abp.FeatureManagement.*
FeatureDefinitionProvider
内容定义
在每个module中都可以定义Feature,遵从如下示例格式
public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var myGroup = context.AddGroup("MyApp");
myGroup.AddFeature("MyApp.PdfReporting", defaultValue: "false");
myGroup.AddFeature("MyApp.MaxProductCount", defaultValue: "10");
}
}
在AbpFeaturesModule中,基类FeatureDefinitionProvider在预初始化时会被扫描到。
[DependsOn(
typeof(AbpLocalizationModule),
typeof(AbpMultiTenancyModule),
typeof(AbpValidationModule),
typeof(AbpAuthorizationAbstractionsModule)
)]
public class AbpFeaturesModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded);
AutoAddDefinitionProviders(context.Services);
}
//...
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
// 收集所有继承自IFeatureDefinitionProvider的类
services.OnRegistred(context =>
{
if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
// 将所有SettingDefinitionProvider保存到AbpFeatureOptions中
services.Configure<AbpFeatureOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
}
如上需要注意AbpFeatureOptions,所有的FeatureDefinitionProvider最终汇总到其内部的集合中,同时查看该类可知也存储着不同值来源的FeatureValueProvider。
public class AbpFeatureOptions
{
public ITypeList<IFeatureDefinitionProvider> DefinitionProviders { get; }
public ITypeList<IFeatureValueProvider> ValueProviders { get; }
public AbpFeatureOptions()
{
DefinitionProviders = new TypeList<IFeatureDefinitionProvider>();
ValueProviders = new TypeList<IFeatureValueProvider>();
}
}
该部分类图简要如下
修改定义
当对于引用的模块内部已有的定义不太符合需要时,可以更改已有的Feature定义,注意需要在Module类中依赖引用的模块。
public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var myApp = context.GetGroupOrNull("MyApp");
if (myApp != null)
{
myApp.
}
}
}
FeatureDefinitionContext
封装了功能分组和功能定义内容,衔接着FeatureDefinitionManager和FeatureDefinitionProvider。内部主要是在字典上做增删查操作。
public class FeatureDefinitionContext : IFeatureDefinitionContext
{
internal Dictionary<string, FeatureGroupDefinition> Groups { get; }
//...
public FeatureGroupDefinition AddGroup(string name, ILocalizableString displayName = null)
{
//...
return Groups[name] = new FeatureGroupDefinition(name, displayName);
}
public FeatureGroupDefinition GetGroupOrNull(string name)
{
//...
return Groups[name];
}
public void RemoveGroup(string name)
{
//...
Groups.Remove(name);
}
}
FeatureDefinitionManager
对于众多的FeatureDefinitionProvider,在这之上封装了一层IFeatureDefinitionManager来管理所有的FeatureDefinitionProvider。
IFeatureDefinitionManager该部分源码简要如下,主要关注从DefinitionProviders转移定义到自身字典属性中。
public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency
{
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value;
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value;
protected AbpFeatureOptions Options { get; }
//...
public virtual IReadOnlyList<FeatureDefinition> GetAll()
{
return FeatureDefinitions.Values.ToImmutableList();
}
public virtual FeatureDefinition GetOrNull(string name)
{
return FeatureDefinitions.GetOrDefault(name);
}
public IReadOnlyList<FeatureGroupDefinition> GetGroups()
{
return FeatureGroupDefinitions.Values.ToImmutableList();
}
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions()
{
var features = new Dictionary<string, FeatureDefinition>();
//循环遍历收集到的所有FeatureGroupDefinitions
foreach (var groupDefinition in FeatureGroupDefinitions.Values)
{
//从FeatureGroupDefinitions中汇总FeatureDefinition到全局字典FeatureDefnitions中
foreach (var feature in groupDefinition.Features)
{
AddFeatureToDictionaryRecursively(features, feature);
}
}
return features;
}
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
{
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
features[feature.Name] = feature;
//Feature拥有子Feature
foreach (var child in feature.Children)
{
AddFeatureToDictionaryRecursively(features, child);
}
}
protected virtual Dictionary<string, FeatureGroupDefinition> CreateFeatureGroupDefinitions()
{
var context = new FeatureDefinitionContext();
using (var scope = _serviceScopeFactory.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider)
.ToList();
//循环遍历所有FeatureDefinitionProvider,收集FeatureGroupDefinition
foreach (var provider in providers)
{
provider.Define(context);
}
}
return context.Groups;
}
}
在注册阶段,扫描到所有的DefinitionProvider注册到AbpFeatureOptions.DefinitionProviders中。
在FeatureDefinitionManager中则读取所有的DefinitionProviders循环调用Definie将所有GroupDefinition保存到FeatureDefinitionManager自身字典属性中,该过程,使用FeatureDefinitionContext来完成承载。
var context = new FeatureDefinitionContext();
provider.Define(context);
Dictionary<string, FeatureGroupDefinition> groups,保存到FeatureDefinitionContext中,各个DefinitionProvider则需要接收一个FeatureDefinitionContext,因此从FeatureDefinitionManager到FeatureDefinitonProvider需要借助一个媒介FeatureDefinitionContext。
public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var group = context.AddGroup("Test Group");
group.AddFeature("BooleanTestFeature1");
}
}
FeatureDefinitionManager->FeatureDefinitionContext->FeatuureDefinitonProvider 3. 后续获取Feature定义则直接从自身属性FeatureDefnitions中获取。
简要概括该部分步骤为:
各模块中完成FeatureDefinitionProvider定义。
扫描所有FeatureDefinitionProvider存储到AbpFeatureOptions中。
初始化FeatureDefinitionManager时,先从AbpFeatureOption中读取定义经过FeatureDefinitionContext转移到FeatureGroupDefinitions字典属性中。
再遍历所有FeatureGroupDefinitions收集具体的FeatureDefinition保存到FeatureDefinitions字典属性中。
读取Feature时,直接从FeatureDefinitions字典属性中读取。
FeatureValueProvider
为了从各处数据源获取Feature定义对应的值,Abp设计了FeatureValueProvider,一个定义的Feature,可以有多个ValueProvider来源,数据库,appsetting.json,文件等各种源头。
在AbpFeatureModule中已提前注册好了几个默认的ValueProvider,对于自定义的ValueProvider也需要在开发的module中采用相同方式手动注册到ValueProviders集合中。
[DependsOn(
typeof(AbpLocalizationModule),
typeof(AbpMultiTenancyModule),
typeof(AbpValidationModule),
typeof(AbpAuthorizationAbstractionsModule)
)]
public class AbpFeaturesModule : AbpModule
{
//...
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<AbpFeatureOptions>(options =>
{
options.ValueProviders.Add<DefaultValueFeatureValueProvider>();
options.ValueProviders.Add<EditionFeatureValueProvider>();
options.ValueProviders.Add<TenantFeatureValueProvider>();
});
//...
}
//...
}
默认的几个ValueProvider各自作用如下,
DefaultValueFeatureValueProvider,从Feature定义中获取默认值,源头是定义本身。
Edition/TenantFeatureValueProvider,分别从版本/租户角度取Feature,最终取的逻辑又移交给IFeatureStore。以租户为例,取得当前租户Id,依赖FeatureStore获取到Feature的具体值。
public class TenantFeatureValueProvider : FeatureValueProvider
{
public const string ProviderName = "T";
public override string Name => ProviderName;
protected ICurrentTenant CurrentTenant { get; }
public TenantFeatureValueProvider(IFeatureStore featureStore, ICurrentTenant currentTenant)
: base(featureStore)
{
CurrentTenant = currentTenant;
}
public override async Task<string> GetOrNullAsync(FeatureDefinition feature)
{
return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString());
}
}
对于ValueProvider,需要对每一个Provider都有唯一的ProviderName,以示区分。如上多个ValueProvider,当在一个ValueProvider中得到值后则不会进入下一个ValueProvider中,如果有优先级的需要,则要考虑注册时的优先级设置。
public class FeatureChecker : FeatureCheckerBase
{
//...
protected virtual async Task<string> GetOrNullValueFromProvidersAsync(
IEnumerable<IFeatureValueProvider> providers,
FeatureDefinition feature)
{
foreach (var provider in providers)
{
//取到有效值后直接返回
var value = await provider.GetOrNullAsync(feature);
if (value != null)
{
return value;
}
}
return null;
}
}
FeatureStore
部分FeatureValueProvider完成了获取值,但是实际的功能依赖于FeatureStore中。
在FeatureManagement模块中实现了FeatureStore,也没有实现具体的取值逻辑,而是依赖FeatureManagementStore。
public class FeatureStore : IFeatureStore, ITransientDependency
{
protected IFeatureManagementStore FeatureManagementStore { get; }
public FeatureStore(IFeatureManagementStore featureManagementStore)
{
FeatureManagementStore = featureManagementStore;
}
public virtual Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{
return FeatureManagementStore.GetOrNullAsync(name, providerName, providerKey);
}
}
此处FeatureStore算是一个Wrapper,FeatureManagementStore承载了对功能的管理职责,而FeatureStore只承担读取职责,从设计上,分开成两个,服务两套系统确实合理。 FeatureStore->FeatureManagementStore->FeatureRepository->FeatureTable
在FeatureManagementStore中最终依赖仓储从数据库表中获取Feature值。
public class FeatureManagementStore : IFeatureManagementStore, ITransientDependency
{
//...
public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{
// ...
(await FeatureValueRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value)
// ...
}
//...
}
此处可以参照FeatureStore,扩展业务自身想要实现的功能,比如自定义Feature表,而不用依赖Abp生成的Feature表。
FeatureChecker
Feature定义好了,其值配置好了,剩下的则是在代码中通过定义,取到值。在业务代码中,直接使用IFeatureChecker来检查是否拥有特定Feature。
public class ProductController : AbpController
{
private readonly IFeatureChecker _featureChecker;
public ProductController(IFeatureChecker featureChecker)
{
_featureChecker = featureChecker;
}
public async Task<IActionResult> Create(CreateProductModel model)
{
var currentProductCount = await GetCurrentProductCountFromDatabase();
//GET THE FEATURE VALUE
var maxProductCountLimit = await _featureChecker.GetOrNullAsync("MyApp.MaxProductCount");
if (currentProductCount >= Convert.ToInt32(maxProductCountLimit))
{
throw new BusinessException(
"MyApp:ReachToMaxProductCountLimit",
$"You can not create more than {maxProductCountLimit} products!"
);
}
//TODO: Create the product in the database...
}
private async Task<int> GetCurrentProductCountFromDatabase()
{
throw new System.NotImplementedException();
}
}
Abp定义了IFeatureChecker,传入一个或多个Feature定义名,返回对应的一组值。
如下简要解析源码,以GetOrNull方法为例。
public class FeatureProvider : FeatureCheckerBase
{
protected AbpFeatureOptions Options { get; }
protected IFeatureDefinitionManager FeatureDefinitionManager{ get; }
protected List<IFeatureValueProvider> Providers { get; }
public FeatureChecker(
IOptions<AbpFeatureOptions> options,
IServiceProvider serviceProvider,
IFeatureDefinitionManager featureDefinitionManager)
{
Options = options.Value;
FeatureDefinitionManager = featureDefinitionManager;
Providers = new Lazy<List<IFeatureValueProvider>>(
() => Options
.ValueProviders
.Select(type => ServiceProvider.GetRequiredService(type) as IFeatureValueProvider)
.ToList(),
true
);
}
public virtual async Task<string> GetOrNullAsync(string name)
{
// 按照name获取FeatureDefinition
var featureDefinition = FeatureDefinitionManager.Get(name);
// 拿到所有ValueProviders
var providers = Enumerable.Reverse(Providers);
if (featureDefinition.AllowedProviders.Any())
{
providers = providers.Where(p => featureDefinition.AllowedProviders.Contains(p.Name));
}
return await GetOrNullValueFromProvidersAsync(providers, featureDefinition);
}
protected virtual async Task<string> GetOrNullValueFromProvidersAsync(
IEnumerable<IFeatureValueProvider> providers,
FeatureDefinition feature)
{
//循环遍历找到有效值直接返回
foreach (var provider in providers)
{
var value = await provider.GetOrNullAsync(feature);
if (value != null)
{
return value;
}
}
return null;
}
}
获取Feature值,核心步骤如下:
在获取值时,先判定是否有该定义
再循环从ValueProvider中获取到对应值
MethodInvocationFeatureCheckerService
除了直接使用IFeatureChecker去检查是否满足要求,还可以采用一些AOP方式拦截,方便重复性的检查代码。为作用于方法层面检查是否满足要求,ABP在FeatureChecker上还封装了一层针对于MethodInvocation的服务,目标明确。
在其中首先判断方法上是否有相应的特性标识(DisableFeatureCheckAttribute或RequiresFeatureAttribute),如果没有则忽视检查过程,如果有,则再调用FeatureChecker获取到实际值,从而判断是否能够有权限进入方法中。
public class MethodInvocationFeatureCheckerService : IMethodInvocationFeatureCheckerService, ITransientDependency
{
private readonly IFeatureChecker _featureChecker;
public MethodInvocationFeatureCheckerService(
IFeatureChecker featureChecker)
{
_featureChecker = featureChecker;
}
public async Task CheckAsync(MethodInvocationFeatureCheckerContext context)
{
// 方法上标注了DisableFeatureCheckAttribute则直接返回
if (IsFeatureCheckDisabled(context))
{
return;
}
//循环检查RequiresFeatureAttribute中标注的Feature
foreach (var requiresFeatureAttribute in GetRequiredFeatureAttributes(context.Method))
{
await _featureChecker.CheckEnabledAsync(requiresFeatureAttribute.RequiresAll, requiresFeatureAttribute.Features);
}
}
protected virtual bool IsFeatureCheckDisabled(MethodInvocationFeatureCheckerContext context)
{
return context.Method
.GetCustomAttributes(true)
.OfType<DisableFeatureCheckAttribute>()
.Any();
}
protected virtual IEnumerable<RequiresFeatureAttribute> GetRequiredFeatureAttributes(MethodInfo methodInfo)
{
var attributes = methodInfo
.GetCustomAttributes(true)
.OfType<RequiresFeatureAttribute>();
if (methodInfo.IsPublic)
{
attributes = attributes
.Union(
methodInfo.DeclaringType
.GetCustomAttributes(true)
.OfType<RequiresFeatureAttribute>()
);
}
return attributes;
}
}
而在AOP拦截方面,面对不同层级的不同方法上有两类方式。
对于常规方法或类上,如AppSerivce等,采用FeatureInterceptor
对于Controller/Method或者Page,采用Filter来处理。
FeatureInterceptor
在方法上标记RequiresFeature特性,例如
public class ReportingAppService : ApplicationService, IReportingAppService
{
[RequiresFeature("MyApp.PdfReporting")]
public async Task<PdfReportResultDto> GetPdfReportAsync()
{
//TODO...
}
}
但想要让特性生效,需要先通过ABP实现的拦截器进行注册拦截,再调用IMethodInvocationFeatureCheckerService来调用服务。
public class FeatureInterceptor : AbpInterceptor, ITransientDependency
{
//...
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
//横切关注点特别判断,绕开Controller/Page中的Method
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.FeatureChecking))
{
await invocation.ProceedAsync();
return;
}
await CheckFeaturesAsync(invocation);
await invocation.ProceedAsync();
}
protected virtual async Task CheckFeaturesAsync(IAbpMethodInvocation invocation)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
//调用服务执行检查
await scope.ServiceProvider.GetRequiredService<IMethodInvocationFeatureCheckerService>().CheckAsync(
new MethodInvocationFeatureCheckerContext(
invocation.Method
)
);
}
}
}
如上存在一个判断逻辑,当判断存在了AbpCrossCuttingConcerns.FeatureChecking,则可认定是Controller/Method或者Page/Method,因此不再ABP的拦截器中处理,则直接进入方法中,避免在Filter中执行过一次检查,又进入到ABP拦截器中再进行一次检查。
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.FeatureChecking))
{
await invocation.ProceedAsync();
return;
}
AbpFeatureActionFilter
对于Controller/Method中,同样是借助AOP实现,只是不由ABP的拦截器管理,而是借助Asp.Net Core提供的Filter来拦截,再调用IMethodInvocationFeatureCheckerService服务。
结合FeatureInterceptor中的源码解释,在Filter中,在进入方法前,先给该方法标记了一个横切关注点AbpCrossCuttingConcerns.FeatureChecking,然后调用检查服务执行检查。
public class AbpFeatureActionFilter : IAsyncActionFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ActionDescriptor.IsControllerAction())
{
await next();
return;
}
var methodInfo = context.ActionDescriptor.GetMethodInfo();
using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.FeatureChecking))
{
var methodInvocationFeatureCheckerService = context.GetRequiredService<IMethodInvocationFeatureCheckerService>();
await methodInvocationFeatureCheckerService.CheckAsync(new MethodInvocationFeatureCheckerContext(methodInfo));
await next();
}
}
}
AbpFeaturePageFilter
与上相同,利用Filter,拦截后调用IMethodInvocationFeatureCheckerService服务。同样也是先在方法上设置一个横切关注点标识,再调用检查服务执行检查。
public class AbpFeaturePageFilter : IAsyncPageFilter, ITransientDependency
{
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{
return Task.CompletedTask;
}
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
if (context.HandlerMethod == null || !context.ActionDescriptor.IsPageAction())
{
await next();
return;
}
var methodInfo = context.HandlerMethod.MethodInfo;
using (AbpCrossCuttingConcerns.Applying(context.HandlerInstance, AbpCrossCuttingConcerns.FeatureChecking))
{
var methodInvocationFeatureCheckerService = context.GetRequiredService<IMethodInvocationFeatureCheckerService>();
await methodInvocationFeatureCheckerService.CheckAsync(new MethodInvocationFeatureCheckerContext(methodInfo));
await next();
}
}
}
类似FeatureChecking的横切关注点标识还有一些。
public static class AbpCrossCuttingConcerns
{
public const string Auditing = "AbpAuditing";
public const string UnitOfWork = "AbpUnitOfWork";
public const string FeatureChecking = "AbpFeatureChecking";
public const string GlobalFeatureChecking = "AbpGlobalFeatureChecking";
//...
}
FeatureManagement
该模块为独立模块,管理Feature值,其内部实现了FeatureStore的整套逻辑。如无特殊需求,建议直接使用上该模块。
扩展
Feature部分的扩展点很多,甚至于可以完全绕开一些已有的功能设计,此处提及几种扩展。
可以扩展FeatureDefinitionContext,现有的功能定义存储在字典中,如果有特殊场景需要想换种实现方式,可以自定义一个FeatureDefinitionContext,但需要注意重写FeatureDefinitionManager中CreateFeatureGroupDefinitions方法(其内部直接实例化了FeatureDefinitionContext类)。
对于FeatureDefinitionManager可以按照需要扩展,可以不再依赖AbpFeatureOptions获取到所有的FeatureDefinitionProvider。
对于FeatureStore可以按照需要扩展,其中实现具体的取值逻辑,取值源头等,不限于关系型数据库表,可从Db,Redis,文件等多种方式,总归使用FeatureStore隔离了具体的取值逻辑。
对于FeatureChecker,可以完全改写检查逻辑。
可以参照RequiresFeature设计符合业务需要的特性,继承IMethodInvocationFeatureCheckerService实现自定义的方法检查逻辑。
还可自定义拦截器,移除默认的FeatureInterceptor,实现自定义的拦截器。
2024-07-05,望技术有成后能回来看见自己的脚步。