01 | 模块化方案一html
02 | 模块化方案二git
01 | 前言github
04 | 强化设计方案app
06 | 最终篇-经过AOP自动链接数据库-完成日志业务模块化
开讲第二篇,本篇代码并不是Copy的ABP,只是参考ABP的功能,进行的实现方案,让代码更加通俗易懂。代码的讲解思路和上一篇同样,可是不引用上篇的写法。工具
仍是老样子,咱们新建一个模块化接口类
新建接口 IAppModule (ps:项目中起的类名和方法名尽可能对标ABP)
测试
/// <summary> /// 应用模块接口定义 /// </summary> public interface IAppModule { /// <summary> /// 配置服务前 /// </summary> /// <param name="context"></param> void OnPreConfigureServices(); /// <summary> /// 配置服务 /// </summary> /// <param name="context">配置上下文</param> void OnConfigureServices(); /// <summary> /// 配置服务后 /// </summary> /// <param name="context"></param> void OnPostConfigureServices(); /// <summary> /// 应用启动前 /// </summary> /// <param name="context"></param> void OnPreApplicationInitialization(); /// <summary> /// 应用启动 /// </summary> /// <param name="context"></param> void OnApplicationInitialization(); /// <summary> /// 应用启动后 /// </summary> /// <param name="context"></param> void OnPostApplicationInitialization(); /// <summary> /// 应用中止 /// </summary> /// <param name="context"></param> void OnApplicationShutdown(); }
新建类 AppModule 继承 IAppModule
public abstract class AppModule : IAppModule { public virtual void OnPreConfigureServices() { } public virtual void OnConfigureServices() { } public virtual void OnPostConfigureServices() { } public virtual void OnPreApplicationInitialization() { } public virtual void OnApplicationInitialization() { } public virtual void OnPostApplicationInitialization() { } public virtual void OnApplicationShutdown() { } }
这一步来完成ABP的DependsOnAttribute,经过特性进行引入模块,
这里参数 params Type[] 由于一个模块会依赖多个模块
新建类 DependsOnAttribute 继承 Attribute
/// <summary> /// 模块依赖的模块 /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class DependsOnAttribute : Attribute { /// <summary> /// 依赖的模块类型 /// </summary> public Type[] DependModuleTypes { get; private set; } public DependsOnAttribute(params Type[] dependModuleTypes) { DependModuleTypes = dependModuleTypes ?? new Type[0]; } }
既然一个模块会包含多个模块的引用,那么就应该有一个存储的方式
新建类 ModuleDescriptor 该类来存储 自身和引用的其余模块
/// <summary> /// 模块描述 /// </summary> public class ModuleDescriptor { private object _instance; /// <summary> /// 模块类型 /// </summary> public Type ModuleType { get; private set; } /// <summary> /// 依赖项 /// </summary> public ModuleDescriptor[] Dependencies { get; private set; } /// <summary> /// 实例 /// </summary> public object Instance { get { if (this._instance == null) { this._instance = Activator.CreateInstance(this.ModuleType); } return this._instance; } } public ModuleDescriptor(Type moduleType, params ModuleDescriptor[] dependencies) { this.ModuleType = moduleType; // 若是模块依赖 为空给一个空数组 this.Dependencies = dependencies ?? new ModuleDescriptor[0]; } }
来到核心步骤,这里咱们写模块管理器,白话就是存储模块和模块操做方法的一个类(同上一篇的StartupModulesOptions)
第一步确定是模块的启动
咱们新建 IModuleManager接口
public interface IModuleManager : IDisposable { /// <summary> /// 启动模块 /// </summary> /// <typeparam name="TModule"></typeparam> void StartModule<TModule>(IServiceCollection services) where TModule : IAppModule; }
紧跟新建类 ModuleManager 继承 IModuleManager, StartModule 先放在一边
这里的思路是:模块是从一个入口的根模块开始的慢慢的造成一个树状的引用关系,咱们首先须要拿到全部的模块引用,并把他们从树叶为起点排列起来,依次注入。
(理解为A=>B=>C 那么注入的顺序应该是 C=>B=>A)
/// <summary> /// 获取模块依赖树 /// </summary> /// <param name="moduleType"></param> /// <returns></returns> protected virtual List<ModuleDescriptor> VisitModule(Type moduleType) { var moduleDescriptors = new List<ModuleDescriptor>(); // 是否必须被重写|是不是接口|是否为泛型类型|是不是一个类或委托 if (moduleType.IsAbstract || moduleType.IsInterface || moduleType.IsGenericType || !moduleType.IsClass) { return moduleDescriptors; } // 过滤没有实现IRModule接口的类 var baseInterfaceType = moduleType.GetInterface(_moduleInterfaceTypeFullName, false); if (baseInterfaceType == null) { return moduleDescriptors; } // 获得当前模块依赖了那些模块 var dependModulesAttribute = moduleType.GetCustomAttribute<DependsOnAttribute>(); // 依赖属性为空 if (dependModulesAttribute == null) { moduleDescriptors.Add(new ModuleDescriptor(moduleType)); } else { // 依赖属性不为空,递归获取依赖 var dependModuleDescriptors = new List<ModuleDescriptor>(); foreach (var dependModuleType in dependModulesAttribute.DependModuleTypes) { dependModuleDescriptors.AddRange( VisitModule(dependModuleType) ); } // 建立模块描述信息,内容为模块类型和依赖类型 moduleDescriptors.Add(new ModuleDescriptor(moduleType, dependModuleDescriptors.ToArray())); } return moduleDescriptors; }
/// <summary> /// 模块接口类型全名称 /// </summary> public static string _moduleInterfaceTypeFullName = typeof(IAppModule).FullName;
新建类 Topological 这块没啥特别要讲的根据连接去看下就行了
/// <summary> /// 拓扑排序工具类 /// </summary> public static class Topological { public static List<T> Sort<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies) { var sorted = new List<T>(); var visited = new Dictionary<T, bool>(); foreach (var item in source) { Visit(item, getDependencies, sorted, visited); } return sorted; } static void Visit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited) { bool inProcess; var alreadyVisited = visited.TryGetValue(item, out inProcess); // 若是已经访问该顶点,则直接返回 if (alreadyVisited) { // 若是处理的为当前节点,则说明存在循环引用 if (inProcess) { throw new ArgumentException("模块出现循环依赖."); } } else { // 正在处理当前顶点 visited[item] = true; // 得到全部依赖项 var dependencies = getDependencies(item); // 若是依赖项集合不为空,遍历访问其依赖节点 if (dependencies != null) { foreach (var dependency in dependencies) { // 递归遍历访问 Visit(dependency, getDependencies, sorted, visited); } } // 处理完成置为 false visited[item] = false; sorted.Add(item); } } }
回到 ModuleManager 新建方法 ModuleSort
/// <summary> /// 模块排序 /// </summary> /// <typeparam name="TModule"></typeparam> /// <returns></returns> public virtual List<ModuleDescriptor> ModuleSort<TModule>() where TModule : IAppModule { // 获得模块树依赖 var moduleDescriptors = VisitModule(typeof(TModule)); // 由于如今获得的数据是从树根开始到树叶 - 实际的注入顺序应该是从树叶开始 因此这里须要对模块进行排序 return Topological.Sort(moduleDescriptors, o => o.Dependencies); }
/// <summary> /// 模块排序 /// </summary> /// <typeparam name="TModule">启动模块类型</typeparam> /// <returns>排序结果</returns> List<ModuleDescriptor> ModuleSort<TModule>() where TModule : IAppModule;
/// <summary> /// 模块明细和实例 /// </summary> public virtual IReadOnlyList<ModuleDescriptor> ModuleDescriptors { get; protected set; } /// <summary> /// 入口 StartModule /// 咱们经过传递泛型进来的 TModule 为起点 /// 查找他的依赖树 /// </summary> /// <typeparam name="TModule"></typeparam> /// <param name="services"></param> public void StartModule<TModule>(IServiceCollection services) where TModule : IAppModule { var moduleDescriptors = new List<ModuleDescriptor>(); var moduleDescriptorList = this.ModuleSort<TModule>(); // 去除重复的引用 进行注入 foreach (var item in moduleDescriptorList) { if (moduleDescriptors.Any(o => o.ModuleType.FullName == item.ModuleType.FullName)) { continue; } moduleDescriptors.Add(item); services.AddSingleton(item.ModuleType, item.Instance); } ModuleDescriptors = moduleDescriptors.AsReadOnly(); }
入口经过调用下面的方法进行模块的方法调用
/// <summary> /// 进行模块的 ConfigurationService 方法调用 /// </summary> /// <param name="services"></param> /// <param name="configuration"></param> /// <returns></returns> public IServiceCollection ConfigurationService(IServiceCollection services, IConfiguration configuration) { foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnPreConfigureServices(); } foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnConfigureServices(); } foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnPostConfigureServices(); } return services; } /// <summary> /// 进行模块的 Configure 方法调用 /// </summary> /// <param name="serviceProvider"></param> /// <returns></returns> public IServiceProvider ApplicationInitialization(IServiceProvider serviceProvider) { foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnPreApplicationInitialization(); } foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnApplicationInitialization(); } foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnPostApplicationInitialization(); } return serviceProvider; } /// <summary> /// 模块销毁 /// </summary> public void ApplicationShutdown() { // todo我以为这里有点问题问 易大师 //var modules = ModuleDescriptors.Reverse().ToList(); foreach (var module in ModuleDescriptors) { (module.Instance as IAppModule)?.OnApplicationShutdown(); } }
固然还漏了一个模块销毁,该方法在主模块被销毁的时候调用(ps: 我我的思路应该是从树叶开始进行,可是ABP对模块顺序进行了反转从根开始进行销毁,因此这里同上)
/// <summary> /// 主模块销毁的时候 销毁子模块 /// </summary> public void Dispose() { this.Dispose(true); } protected virtual void Dispose(bool state) { this.ApplicationShutdown(); }
模块管理器写完了,那么这个方法如何调用呢来写咱们的 Extensions
新建 RivenModuleServiceCollectionExtensions 类,让其完成ConfigurationService方法的模块调用
/// <summary> /// 模块服务扩展 /// </summary> public static class RivenModuleServiceCollectionExtensions { /// <summary> /// 添加Riven模块服务 /// </summary> /// <typeparam name="TModule"></typeparam> /// <param name="services"></param> /// <param name="configuration"></param> /// <returns></returns> public static IServiceCollection AddRivenModule<TModule>(this IServiceCollection services, IConfiguration configuration) where TModule : IAppModule { var moduleManager = new ModuleManager(); // 将模块都查询排序好 moduleManager.StartModule<TModule>(services); // 调用模块 和 子模块的ConfigurationService方法 moduleManager.ConfigurationService(services, configuration); // 注入全局的 IModuleManager services.TryAddSingleton<IModuleManager>(moduleManager); return services; } }
新建 RivenModuleIApplicationBuilderExtensions 类 ,让其完成Configuration方法的模块调用
public static class RivenModuleIApplicationBuilderExtensions { /// <summary> /// 使用RivenModule /// </summary> /// <param name="serviceProvider"></param> /// <returns></returns> public static IServiceProvider UseRivenModule(this IServiceProvider serviceProvider) { var moduleManager = serviceProvider.GetService<IModuleManager>(); return moduleManager.ApplicationInitialization(serviceProvider); } }
新建一个测试项目,引入写好的模块化类库,在 ConfigureServices 中调用
services.AddRivenModule<MyAppStartupModule>(Configuration);
Configure 中调用
app.ApplicationServices.UseRivenModule();
模块销毁演示(ps:这个是演示效果、实际是在项目中止的时候进行。)
app.Map("/ApplicationShutdown", _ => { _.Run((context) => { var moduleManager = app.ApplicationServices.GetService<IModuleManager>(); moduleManager.ApplicationShutdown(); return Task.FromResult(0); }); });
新建 MyAppStartupModule、TestModuleA、TestModuleB 继承AppModule。
MyAppStartupModule做为入口模块 引用 A => B 而后在模块方法中打印 Console.WriteLine 看效果
新建 ApplicationInitializationContext 类
public class ApplicationInitializationContext { public IServiceProvider ServiceProvider { get; } public IConfiguration Configuration { get; } public ApplicationInitializationContext([NotNull] IServiceProvider serviceProvider, [NotNull] IConfiguration configuration) { ServiceProvider = serviceProvider; Configuration = configuration; } }
新建 ApplicationShutdownContext 类
public class ApplicationShutdownContext { public IServiceProvider ServiceProvider { get; } public ApplicationShutdownContext([NotNull] IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; } }
新建 ServiceConfigurationContext 类
public class ServiceConfigurationContext { public IServiceCollection Services { get; protected set; } public IConfiguration Configuration { get; protected set; } public ServiceConfigurationContext(IServiceCollection services, IConfiguration configuration) { Services = services; Configuration = configuration; } }
修改 IAppModule 接口, 模块和实现都本身手动都同步一下
/// <summary> /// 应用模块接口定义 /// </summary> public interface IAppModule { /// <summary> /// 配置服务前 /// </summary> /// <param name="context"></param> void OnPreConfigureServices(ServiceConfigurationContext context); /// <summary> /// 配置服务 /// </summary> /// <param name="context">配置上下文</param> void OnConfigureServices(ServiceConfigurationContext context); /// <summary> /// 配置服务后 /// </summary> /// <param name="context"></param> void OnPostConfigureServices(ServiceConfigurationContext context); /// <summary> /// 应用启动前 /// </summary> /// <param name="context"></param> void OnPreApplicationInitialization(ApplicationInitializationContext context); /// <summary> /// 应用启动 /// </summary> /// <param name="context"></param> void OnApplicationInitialization(ApplicationInitializationContext context); /// <summary> /// 应用启动后 /// </summary> /// <param name="context"></param> void OnPostApplicationInitialization(ApplicationInitializationContext context); /// <summary> /// 应用中止 /// </summary> /// <param name="context"></param> void OnApplicationShutdown(ApplicationShutdownContext context); }
修改 ModuleManager的 ConfigurationService、ApplicationInitialization、ApplicationShutdown 方法给调用传递对应参数
这部分代码我就不贴了,会的大佬都能本身写,想看的去个人github直接下载源码看吧,麻烦老板们给点个星星!!!