Skip to content

模块化

HMX平台采用模块化架构设计,支持通过插件式的方式扩展系统功能。本文档帮助开发者了解模块系统的约定和规范,确保开发的DLL能被系统正确识别和加载。

模块发现机制

系统启动时会自动扫描应用程序目录下的所有DLL文件,寻找实现了 IHiMindModule 接口的类。被扫描到的DLL会自动加载并注册为系统模块。

扫描规则

规则说明
扫描目录应用程序根目录(AppDomain.CurrentDomain.BaseDirectory
文件类型仅扫描 .dll 文件
排除前缀DevExpress.Castle.Microsoft.System.JetBrains.Newtonsoft.Oracle.
类要求非抽象、非泛型的具体类
接口要求必须实现 IHiMindModule 接口

重要:你的DLL必须放在应用程序的运行目录下,且文件名不能以排除前缀开头,否则不会被系统扫描到。

创建模块

基本步骤

  1. 创建一个类,继承自 HiMindModule
  2. 重写必要的生命周期方法
  3. 将DLL输出到应用程序目录

模块示例

csharp
using Hmx.Core.Modularity;
using Microsoft.Extensions.DependencyInjection;

namespace YourCompany.YourModule.Runtime
{
    /// <summary>
    /// 你的业务模块
    /// </summary>
    public class YourBusinessModule : HiMindModule
    {
        /// <summary>
        /// 模块名称(可选,默认取类名去掉"Module"后缀)
        /// </summary>
        public override string Name { get; } = "你的业务模块";

        /// <summary>
        /// 预初始化阶段:注册依赖项、配置服务
        /// </summary>
        public override Task PreInitialize(ApplicationPreInitializeContext context)
        {
            var services = context.Services;

            // 注册服务
            services.AddScoped<IYourService, YourService>();
            services.AddSingleton<IYourRepository, YourRepository>();

            return base.PreInitialize(context);
        }

        /// <summary>
        /// 初始化阶段:模块核心功能设置
        /// </summary>
        public override Task Initialize(ApplicationInitializationContext context)
        {
            // 可选:执行初始化逻辑
            return base.Initialize(context);
        }

        /// <summary>
        /// 后置初始化阶段:其他模块初始化完成后的操作
        /// </summary>
        public override Task PostInitialize(ApplicationInitializationContext context)
        {
            // 可选:启动后台任务、清理工作
            return base.PostInitialize(context);
        }

        /// <summary>
        /// 关闭阶段:释放资源
        /// </summary>
        public override Task Shutdown(ApplicationShutdownContext context)
        {
            // 可选:清理资源
            return base.Shutdown(context);
        }
    }
}

模块生命周期

系统按顺序执行以下阶段:

PreInitialize → Initialize → PostInitialize → Shutdown
阶段时机典型用途
PreInitialize应用启动早期注册依赖项、绑定配置
Initialize依赖注入构建完成后初始化核心服务
PostInitialize所有模块初始化完成启动后台任务、执行依赖其他模块的逻辑
Shutdown应用关闭时释放资源、清理工作

服务自动注册

除了在模块中手动注册服务外,系统还支持通过标记接口实现自动注册。只要实现对应的标记接口,服务会在 AutoRegister 阶段自动注册到依赖注入容器。

标记接口

接口生命周期说明
ISingletonDependency单例全局唯一实例,整个应用生命周期
IScopedDependency作用域每个请求/作用域一个实例
ITransientDependency瞬时每次请求创建新实例

自动注册示例

csharp
using Hmx.Core.DependencyInjection;

namespace YourCompany.YourModule.Services
{
    /// <summary>
    /// 单例服务示例
    /// </summary>
    public interface ICacheService : ISingletonDependency
    {
        T Get<T>(string key);
        void Set<T>(string key, T value);
    }

    public class RedisCacheService : ICacheService
    {
        // 实现...
    }

    /// <summary>
    /// 作用域服务示例
    /// </summary>
    public interface IOrderService : IScopedDependency
    {
        Task<Order> GetOrderAsync(string id);
    }

    public class OrderService : IOrderService
    {
        // 实现...
    }

    /// <summary>
    /// 瞬时服务示例
    /// </summary>
    public interface IEmailSender : ITransientDependency
    {
        Task SendAsync(string to, string subject, string body);
    }

    public class EmailSender : IEmailSender
    {
        // 实现...
    }
}

注意:自动注册使用 TryAdd 方式,如果接口已注册则不会重复注册。如需覆盖已有注册,请在模块的 PreInitialize 中手动注册。

模块依赖与加载顺序

依赖声明

模块之间可以存在依赖关系。系统会根据程序集的引用关系自动分析依赖:

csharp
// ModuleA 被 ModuleB 引用
// 则 ModuleA 会在 ModuleB 之前加载
public class ModuleA : HiMindModule { }
public class ModuleB : HiMindModule { } // 引用了 ModuleA 所在的程序集

加载顺序

系统按以下规则确定模块加载顺序:

  1. 无依赖的模块优先加载(Order = 0)
  2. 有依赖的模块在所有依赖之后加载(Order = 依赖模块最大Order + 1)
  3. 同级模块按发现顺序加载

常见问题排查

DLL不被识别

问题原因解决方案
DLL未被扫描文件名以排除前缀开头重命名DLL,避免 Microsoft.System. 等前缀
类未被发现不是具体类确保类是非抽象、非泛型的具体类
接口未实现未实现 IHiMindModule继承 HiMindModule 或直接实现 IHiMindModule
DLL不在目录未复制到运行目录确保DLL输出到应用程序根目录

服务未注册

问题原因解决方案
服务无法解析未注册到DI容器检查模块 PreInitialize 或使用标记接口
自动注册不生效标记接口使用错误确保实现的是 ISingletonDependency 等标记接口
服务被覆盖多处注册使用 TryAdd 或调整注册顺序

调试技巧

Program.cs 中添加日志输出,查看模块加载情况:

csharp
var engine = builder.InitializeModules<WebapiStartupModule>();

// 输出所有已加载的模块
foreach (var module in engine.Modules)
{
    Console.WriteLine($"已加载模块: {module.Name} - {module.Assembly.FullName}");
}

最佳实践

  1. 单一职责:每个模块专注于一个业务领域
  2. 松耦合:模块间通过接口交互,避免直接依赖实现
  3. 合理命名:模块类名以 Module 结尾,如 OrderModule
  4. 命名空间规范:使用 {公司}.{项目}.{模块名}.Runtime 命名空间
  5. 文档注释:为模块和关键服务添加XML文档注释

现有模块参考

模块所在项目说明
WebapiStartupModuleHmx.Service.StartupWebAPI服务启动模块
AdminServiceModuleHmx.Service.Admin.Impl系统管理服务模块
ServiceModuleHmx.Service.AIMind.ImplAI助手服务模块
HmxCoreModuleHmx.Core核心基础模块

HiMind 工业互联网平台 技术文档