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转移定义到自身字典属性中。
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集合中。
在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。
在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方法为例。
如下简要解析源码,以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获取到实际值,从而判断是否能够有权限进入方法中。
在其中首先判断方法上是否有相应的特性标识(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,望技术有成后能回来看见自己的脚步。