插件系统
插件系统是 SharwAPI 的核心。本文将解析插件是如何被主程序发现、加载以及管理其生命周期的。
加载机制
主程序通过 AssemblyLoadContext (ALC) 技术来实现插件的隔离加载。该过程完全自动化,包含以下四个步骤:
- 目录扫描: 主程序启动时,自动检查根目录下的
plugins文件夹。 - 上下文创建: 为扫描到的每一个插件
.dll文件,创建一个独立的PluginLoadContext(隔离环境)。 - 隔离加载:
- 将插件程序集及其依赖加载到各自的上下文中。
- 智能解析: 插件自身的依赖(如
Newtonsoft.Json v13.0)仅在当前上下文中可见;而共享库(如Sharw.Contracts)会自动回退到主程序上下文,确保类型兼容。
- 实例创建: 在隔离环境中查找实现了
IApiPlugin接口的类,并创建其实例。- 当前限制:每个插件 DLL 仅会加载第一个被发现的
IApiPlugin实现类。 - 建议:一个 DLL 只放一个插件主类,避免多个实现导致只有首个实现被加载。
- 当前限制:每个插件 DLL 仅会加载第一个被发现的
- 依赖检查:
- 遍历所有已加载的插件,检查其
Dependencies属性声明的依赖项。 - 存在性检查: 确保依赖的插件已加载。
- 版本兼容性: 验证依赖插件的
Version是否满足声明的版本范围 (支持[1.0, 2.0)或1.*等格式)。 - 自动卸载: 若依赖未满足,主程序会记录错误日志并将该插件从活动列表中移除,防止运行时错误。
- 遍历所有已加载的插件,检查其
- 插件运行: 通过依赖检查的插件正式进入运行状态。
关于依赖问题
从 v0.2.0 版本开始的 SharwAPI 采用 独立上下文 (AssemblyLoadContext) 技术来实现插件的隔离加载。 这彻底解决了不同插件使用同一个第三方库的不同版本等依赖问题,因为它们各自的隔离环境里运行,互不干扰。
但 v0.2.0 以前版本的 SharwAPI 采用 共享加载上下文 (Shared Context) 策略。 这意味着所有插件和主程序运行在同一个“环境”中。如果插件 A 和插件 B 引用了同一个第三方库但版本不同,可能会导致版本冲突或运行时错误。
隔离规范
为了防止插件之间互相干扰,SharwAPI 采用以下隔离策略:
配置隔离 (物理隔离)
SharwAPI 摒弃了传统的全局 appsettings.json 混合配置模式,转而采用 独立文件配置。
- 机制:主程序启动时,会自动读取
config/目录下与插件同名的 JSON 文件(例如config/sharw.demo.json)。 - 注入:在调用
RegisterServices时,传递给插件的configuration参数将 仅包含 该文件的内容。 - 优势:插件无需担心配置键名冲突(Key Collision),也无需使用
GetSection进行多层嵌套,直接读取根节点即可。
路由的划分 (逻辑隔离)
在 RegisterRoutes 中定义接口时,强烈建议 开启自动前缀模式 (UseAutoRoutePrefix = true)。 这能确保你的所有 API 都被安全地限制在 /api/{插件名}/ 之下,彻底杜绝路由冲突。
服务的命名 (逻辑隔离)
虽然代码逻辑已隔离,但 依赖注入 (DI) 容器 依然是全局共享的。在 RegisterServices 中注册服务时,仍建议使用 插件名 作为前缀。
- 推荐:
services.AddHttpClient("sharw.demo.client", ...) - 避免:
services.AddHttpClient("client", ...)(容易被其他插件覆盖)
生命周期管理
插件的实例在整个应用运行期间是 全局唯一 (Singleton) 的。 这意味着 IApiPlugin 的实现类在内存中只有一个对象,主程序会一直持有它直到关闭。
关键生命周期钩子
插件通过实现以下四个方法,介入主程序的不同启动阶段:
RegisterServices (注册服务)
- 阶段: 应用构建前。
- 作用: 向全局容器注册依赖服务(如数据库服务、后台任务)。
Configure (配置管道)
- 执行时机: 应用构建后,启动前。
- 作用: 向 HTTP 请求处理管道中插入中间件(如日志记录、鉴权逻辑)。
RegisterRoutes (映射路由)
- 执行时机: 中间件配置后。
- 作用: 定义面向用户的业务 API 接口(如
/api/demo/hello)。 - 注意: 所有 HTTP 请求都会先经过
Configure中定义的中间件,最后才会到达这里。详情请参考 请求处理流程
RegisterManagementEndpoints (挂载后台)
- 执行时机: 由调用方按需调用。
- 作用: 定义仅供管理员使用的运维接口(如
/admin/plugin/demo/status)。这些接口通常用于检查插件是否存活、强制重载配置等运维操作,不面向普通用户开放。详情请参考 管理接口
开发工具说明
- Swagger / OpenAPI UI 仅在开发环境启用:主程序仅在
IsDevelopment()条件下挂载 Swagger 中间件与 UI。 - 在生产环境中默认不会暴露 Swagger 页面,如需 API 文档能力应自行提供受控方案(例如受鉴权保护的文档网关)。