JAR 开发指南
面向 第三方或内部团队:在 不改动 MMS 主工程源码 的前提下,编写独立 Maven 工程,产出符合契约的 JAR,由宿主(mms-plugin-host)扫描加载。第一次从 0 跑通示例,请先读 第 0 节(阅读画像、术语、mms-plugin-api 安装、闭环步骤与排障)。
运维配置与 HTTP 接口见 JAR 插件入门;管理端前端元数据与路由约定见 JAR 插件路由协议。
新手推荐阅读顺序(按顺序翻页即可)
- 0. 第一次写插件:先知道自己要会什么、术语一遍过、
mms-plugin-api安装、10 分钟闭环、排障表。 - JAR 插件入门:宿主
mms.plugin配置、磁盘目录、/system/pluginHost/*、管理端菜单一览。 - 本文第 1~10 节:前置条件、工程形态、
plugin.json/ SPI、Redis/库与「宿主能力门面」规划(第 5.2~5.2.1 节)、打包与自检清单。 - 进阶(有可独立发布的前端时再读):JAR 插件路由协议。
还不会启动 mms-admin? 请先完成 项目导入与启动,再回来做 第 0.4 步的闭环。
0. 第一次写插件?先读本节
面向 能打开 IDE、愿意敲命令行 的读者:把「隐含门槛」摊开,并给一条 从克隆仓库到 health 能查到 的串联路径。本节 不 替代 Java / Maven 系统教程。
0.1 默认你已经会什么;完全零基础请先补哪里
| 能力 | 说明 |
|---|---|
| Java | 能新建类、改包名、看懂接口与 @Override。本仓与文档按 JDK 21(与主工程 java.version 一致)。若你只用过更旧版本,请先装好 21 再跟示例。 |
| Maven | 能在项目根目录执行 mvn -v,理解「模块」「pom.xml」「打包产物在 target/」。还不会的话,请先完成任一公开的 Maven 入门(30~60 分钟),再回来看本节。 |
| 不用先会 | Spring 在插件入口里 不会 帮你 @Autowired;第一轮可以 完全不理会 Spring,只写 MmsPlugin#onLoad。 |
文档能帮你的:照着契约和示例 JAR,在主仓里跑通一条闭环;不能帮你的:从零讲授语言语法或 Maven 生命周期。
0.2 术语速查:provided、SPI、JAR 里要放什么
provided(Maven) 表示 「编译和测试时用这个依赖,打进最终 JAR 时排除」。mms-plugin-api必须provided:运行时由宿主 已加载的 API 提供同一套接口;若打成 fat-jar 把 API 又打进来一份,容易出现 类重复、行为怪异。第一轮调试请保持与mms-plugin-sample-health相同的依赖写法。SPI(
ServiceLoader) Java 约定:在 JAR 里放META-INF/services/<接口全限定名>文件,一行一个实现类全限定名;宿主用ServiceLoader在你的插件ClassLoader里创建实例。本项目的入口接口是com.sxpcwlkj.plugin.MmsPlugin,对应文件路径固定为:META-INF/services/com.sxpcwlkj.plugin.MmsPlugin常见笔误:文件名少写一段包名、一行里多了空格、实现类与plugin.json的entryClass不一致。JAR 内必备资源(最小集)
META-INF/mms/plugin.json(或用兼容路径plugin.json,推荐前者)- 上述 MmsPlugin 的 SPI 文件
entryClass指向的.class可选:实现PluginHealthContributor时再加对应META-INF/services/...PluginHealthContributor文件。
0.3 mms-plugin-api 不在 Maven 中央仓:install 保姆步骤
独立 Git 仓库写插件时,IDE 会报 找不到 com.sxpcwlkj:mms-plugin-api,不是 你网络问题,而是 该坐标未发布到中央仓库。
在 已克隆的 MMS 主仓根目录执行(只需在你本机成功执行 一次;换版本升级主仓后若 revision 变了,可再执行):
cd /path/to/mms
mvn -pl mms-modules/mms-plugin-api -am install -DskipTests~/.m2/repository下会出现对应版本的mms-plugin-api,你的独立插件工程里的<version>必须与主仓revision(见根pom)一致。- 若命令报错:先确认 JDK 21、Maven 已加入
PATH,且当前目录是 带mms-modules的主仓根。
主仓内 mms-plugins/xxx 子模块联调时,通常 不需要 单独 install API,用 Reactor 一起编即可(见 第 7 节 打包命令)。
0.4 快速上手:从克隆到 health 可查
以下假设:本机已能启动 mms-admin(若尚未跑通过,请先跟 项目导入与启动 把后端与库表跑起来),且你有一个具备 super_admin 的账号(调用 /system/pluginHost/* 必需;token 可经登录接口或浏览器里已有会话按需复制)。调试接口也可用主工程开启的 Swagger / OpenAPI(文档分组含「插件宿主」时)代替手写 curl。
管理端菜单里「插件市场」若未出现,可执行主仓增量 SQL(见 JAR 插件入门 管理端菜单 小节);没有菜单不影响 你用 HTTP 完成安装与验证。
① 克隆主仓、确认版本
git clone <你的 mms 远程地址> mms && cd mms打开 仓库根 pom.xml,记下 revision(文档编写时常见为 21,以你克隆到的仓库为准)。后面 plugin.json 的 requiresMms.revisionMin、mms.plugin.host-mms-revision 都要与当前主工程版本 对齐。
② 打开宿主开关(application.yml)
在 mms-admin 使用的配置里确认存在 (或与下文等价的 dev/profile 合并结果):
mms:
plugin:
enabled: true
root-dir: ${user.home}/mms/plugins
host-mms-revision: ${revision}含义简述:root-dir 下将按 pluginId / version / lib 落盘;host-mms-revision 须与根 revision 一致。详见 JAR 插件入门。
③ 只编示例插件(不改代码也能通)
cd /path/to/mms
mvn -pl mms-plugins/mms-plugin-sample-health -am package -DskipTests产物:mms-plugins/mms-plugin-sample-health/target/*.jar(任取带示例名的可执行 JAR,非 -sources)。
④ 把 JAR 放到宿主能扫到的目录(二选一)
方式 A — 页面上传 / 接口安装(推荐第一次) 使用
POST /system/pluginHost/install,表单字段file上传上述 JAR;宿主会写入root-dir下约定结构并 重载。curl 示例见 JAR 插件入门。方式 B — 纯手造目录 示例
plugin.json里id为mms.plugin.sample-health,version为1.0.0,则目录为:text${user.home}/mms/plugins/mms.plugin.sample-health/1.0.0/lib/<把打好的 jar 放这里>.jar然后调用
POST /system/pluginHost/reload,或 重启mms-admin(生产环境更稳妥)。
⑤ 验证
GET /system/pluginHost/status:plugins列表中应出现示例插件;pluginsRootReady为true。GET /system/pluginHost/health:应能看到示例插件返回的 JSON 片段(示例工程实现了PluginHealthContributor)。
若此处仍为空,先看 status.resolvedPluginsRoot 与你手放 JAR 的路径是否一致,再查应用日志里 PluginLifecycleManager / PluginException 的报错。
0.5 常见问题速查
| 现象 | 建议 |
|---|---|
revision / requiresMms 校验失败 | 根 pom 的 revision、mms.plugin.host-mms-revision、plugin.json 里 revisionMin/revisionMax 三者对齐;改完重新打包插件或更新磁盘上的 plugin.json(以你当前流程为准)。 |
报 SPI 未发现、entryClass 找不到 | 检查 META-INF/services/com.sxpcwlkj.plugin.MmsPlugin 是否 恰好一行、无 BOM、类名与 entryClass 一致;JAR 里是否真有 META-INF/mms/plugin.json。 |
pluginsRootReady: false | root-dir 所在盘符无写权限、路径被安全软件拦截,或配置未加载到当前 profile。 |
| 改完代码却始终加载旧逻辑 | 确认替换的是 对应 pluginId/version 下 lib 里 的 JAR;需要 reload 或重启;热替换在能力边界内 不保证 与重启等价。 |
| 独立仓库仍解析不了 API | 确认已执行 第 0.3 节 的 install,且插件 pom 里 mms-plugin-api 版本 = 主仓 revision。 |
0.6 下一步:菜单与带界面的插件(第二步)
- 超级管理员菜单、市场页入口:见 JAR 插件入门 — 管理端菜单。
plugin.json的frontend、与 mms-ui 路由/菜单对齐:属 进阶,见 JAR 插件路由协议;不要 与「第一轮只跑通 SPI + health」混在同一天折腾。
1. 前置条件
| 项 | 说明 |
|---|---|
| JDK | 21(与主工程 java.version 一致) |
| 宿主 | mms.plugin.enabled=true,且 mms.plugin.host-mms-revision 与根 pom 的 revision(如 21)一致 |
| 依赖坐标 | com.sxpcwlkj:mms-plugin-api:provided 作用域,版本与主工程 revision 相同(如 21) |
| 契约 | JAR 内含 META-INF/mms/plugin.json,且 entryClass 所指向的类 实现 MmsPlugin 并注册 SPI |
mms-plugin-api 未发布到 Maven 中央库,需从 MMS 主仓先 install 到本地仓库,或在主仓内用 Reactor 一起编(见下文「打包」)。
2. 推荐工程形态
2.1 形态 A:主仓内子模块(联调最省事)
复制 mms-plugins/mms-plugin-sample-health 为模板;业务插件放在 仓库根目录 下 mms-plugins 聚合模块中(与 mms-modules 同级),与宿主契约 mms-plugin-api 解耦:
mms/ ← 根 pom
pom.xml
mms-modules/
pom.xml
mms-plugin-api/
mms-plugin-host/
...
mms-plugins/ ← 插件 JAR 聚合(packaging pom),与 mms-modules 同级
pom.xml
mms-plugin-sample-health/
your-plugin-xxx/ ← 新插件:在 mms-plugins/pom.xml 中增加 <module>新模块的 pom.xml 与示例类似:parent 指向 mms-plugins,只依赖 mms-plugin-api(provided);并在 mms-plugins/pom.xml 的 <modules> 中登记该模块。
在聚合工程中登记新子模块(mms-plugins/pom.xml):
<modules>
<module>mms-plugin-sample-health</module>
<module>your-plugin-xxx</module>
</modules>子模块 pom.xml 演示(与 mms-plugin-sample-health 同款结构):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sxpcwlkj</groupId>
<artifactId>mms-plugins</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>jar</packaging>
<artifactId>your-plugin-xxx</artifactId>
<description>业务插件说明</description>
<dependencies>
<dependency>
<groupId>com.sxpcwlkj</groupId>
<artifactId>mms-plugin-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>2.2 形态 B:完全独立的 Git 仓库
任意目录新建 Maven 工程,groupId / artifactId 自定;不要把 com.sxpcwlkj:mms-plugin-api 写成 compile 打进最终 JAR(保持 provided)。
your-mms-plugin/
pom.xml
src/main/java/.../YourPlugin.java
src/main/resources/META-INF/mms/plugin.json
src/main/resources/META-INF/services/com.sxpcwlkj.plugin.MmsPlugin
# 可选:META-INF/services/com.sxpcwlkj.plugin.PluginHealthContributor先在本机安装 API(在主仓根目录执行一次即可):
cd /path/to/mms
mvn -pl mms-modules/mms-plugin-api install -DskipTests之后独立工程即可解析 com.sxpcwlkj:mms-plugin-api:21。
单独打你的插件包时 不要只对子模块 -f pom.xml 裸跑(拿不到兄弟模块),应保证本地 ~/.m2 中已有上述 install 的 API,或使用主仓:
cd /path/to/mms
mvn -pl mms-plugins/your-plugin-xxx -am package -DskipTests2.3 打包与交付形态(瘦 JAR / standalone)
与「工程放哪」正交:同一套 plugin.json + SPI,产物可以是 瘦包 或 fat/standalone,由 依赖是否必须跟插件进 lib 决定。
| 交付形态 | 典型场景 | 说明 |
|---|---|---|
| 瘦 JAR | 仅依赖 mms-plugin-api(provided) 与宿主已有类库能覆盖的运行时 | mvn package 默认瘦包即可;同一 pluginId/version 的 lib/ 下可并列多个 JAR,宿主同一 ClassLoader 加载(见 第 5.5 节)。 |
| standalone(shade 等) | 需自带 DeepL SDK、整站 doc 业务 等宿主未提供的传递依赖 | 使用 maven-shade-plugin 等打出 *-standalone.jar;上传 install 往往只收 一个文件,须把依赖打进去,否则会 NoClassDefFoundError。详见 DeepL、文档站合并 等分册。 |
2.4 运行与扩展类型(便于选型)
| 维度 | 可选值 / 说明 |
|---|---|
plugin.json · runtimeMode | SPI_ONLY(未写该字段时 默认,兼容旧插件):仅 MmsPlugin SPI,不向宿主 Web 端口挂业务路由。 HOST_MVC:可向宿主主端口暴露 /plugin/{pluginId}/... 形态接口(示例见 示例健康插件)。 INDEPENDENT_PROCESS:契约内预留 独立进程 + 端口;当前宿主侧以 元数据与 SPI 为主,是否起独立进程以主仓实现与版本说明为准,勿仅按字段名假设行为。 |
kind | extension(默认):业务扩展,一般有 entryClass + SPI。 library:给其他插件依赖的库包;可无 entryClass / 入口 SPI,由宿主依赖图解析(见 mms-plugin-api PluginKind)。 |
与 HostServices 对齐 | hostServicesContractVersion:未写则按 1 处理;使用 PluginRuntimeContext 上缓存等宿主能力 时,版本须与宿主契约一致(分册里常见为 1 或 3,以各插件为准)。 |
更多 权限码、HOST_MVC 路径、invoke 等运维向约定见 JAR 插件分册总览。
3. 源码与 JAR 内目录结构
最小可加载布局示例:
src/main/java/com/example/demo/DemoPlugin.java
src/main/resources/META-INF/mms/plugin.json
src/main/resources/META-INF/services/com.sxpcwlkj.plugin.MmsPluginplugin.json:也可放在 JAR 根下的plugin.json(兼容),推荐仍用META-INF/mms/plugin.json。- SPI:文件名为固定路径
META-INF/services/com.sxpcwlkj.plugin.MmsPlugin内容为 一行一个 实现类全限定名(与entryClass一致或宿主按entryClass绑定,示例工程两者一致即可)。
主仓 META-INF/mms/plugin.example.json 展示完整字段(含 requiresMms、frontend、dependencies);最小示例如 mms-plugins/mms-plugin-sample-health 的 plugin.json。
最小可加载 plugin.json(与示例工程一致,可按需改名):
{
"id": "com.example.mms.demo",
"version": "1.0.0",
"name": "演示插件",
"description": "联调宿主用",
"requiresMms": {
"revisionMin": 21
},
"entryClass": "com.example.mms.demo.DemoPlugin",
"kind": "extension"
}SPI:META-INF/services/com.sxpcwlkj.plugin.MmsPlugin(单列一行,与 entryClass 一致):
com.example.mms.demo.DemoPlugin入口类演示(实现 MmsPlugin;可选再实现 PluginHealthContributor 并单独注册对应 services 文件):
package com.example.mms.demo;
import com.sxpcwlkj.plugin.MmsPlugin;
import com.sxpcwlkj.plugin.PluginHealthContributor;
import com.sxpcwlkj.plugin.PluginRuntimeContext;
public class DemoPlugin implements MmsPlugin, PluginHealthContributor {
@Override
public void onLoad(PluginRuntimeContext context) {
// 数据目录:context.pluginDataDirectory()
// 安装根:context.pluginInstallRoot()
}
@Override
public String health() {
return "{\"status\":\"UP\",\"plugin\":\"demo\"}";
}
}可选:META-INF/services/com.sxpcwlkj.plugin.PluginHealthContributor(仅当类implements该接口时需要;可与上类相同 FQCN):
com.example.mms.demo.DemoPlugin4. plugin.json 描述文件(格式与字段)
宿主读取 META-INF/mms/plugin.json(或兼容 plugin.json,见 JAR 插件入门)。JSON 根对象与主仓 PluginDescriptor 一一对应;未知字段默认忽略(便于向前兼容)。权威示例见 mms-plugin-api → META-INF/mms/plugin.example.json。
4.1 标识、版本与说明(「叫什么」)
| 字段 | 类型 | 含义与约定 |
|---|---|---|
id | 字符串 | 唯一标识,建议 反向域名(如 com.acme.myplugin),与磁盘目录 pluginId 对应;含 / 等特殊字符时宿主会 安全化 目录名(见 第 7.1 节)。 |
version | 字符串 | 插件语义化版本(如 1.0.0),与安装目录 <version>/ 一致;市场 多版本并存 时以该字符串区分。 |
name | 字符串 | 展示名称(插件市场、列表标题等),可与 Maven artifactId 不同。 |
description | 字符串 | 简短说明;支持一句到一小段,具体展示长度以 UI 为准。 |
id / version 不要混用
id不是 MavengroupId:artifactId;artifact 名可变,id一经上线尽量稳定。- 升版通常只改
version,并在宿主lib/下使用 新版本目录;与requiresMms、revisionMin联动见 第 0.5 节 排障表。
4.2 与宿主 MMS 的兼容:requiresMms
| 字段 | 类型 | 含义 |
|---|---|---|
revisionMin | 整数 | 最低兼容的 MMS 根 revision(含)。 |
revisionMax | 整数 | 最高兼容的 revision(含);省略 表示只校验下限。 |
springBoot | 字符串 | 可选;期望的 Spring Boot 版本标识(如 3.5.8),匹配策略以宿主实现为准。 |
4.3 入口、形态与运行模式
| 字段 | 类型 | 含义 |
|---|---|---|
entryClass | 字符串 | MmsPlugin 实现类 全限定名;kind: library 时可省略。须与 META-INF/services/...MmsPlugin 内容一致(见 第 3 节)。 |
kind | 字符串 | extension(默认)或 library(库包,见 第 2.4 节)。 |
runtimeMode | 枚举字符串 | SPI_ONLY / HOST_MVC / INDEPENDENT_PROCESS;省略等价于 SPI_ONLY(见 第 2.4 节)。 |
hostServicesContractVersion | 整数 | 声明依赖的 HostServices 契约版本;省略按 1。 |
仅当 runtimeMode 为 INDEPENDENT_PROCESS 时(且宿主支持该模式时)可能使用:
| 字段 | 类型 | 含义 |
|---|---|---|
independentPort | 整数 | 对外端口 1024–65535。 |
mainClass | 字符串 | 独立进程 主类 全限定名。 |
4.4 数据表、依赖指纹与其它元数据
| 字段 | 类型 | 含义 |
|---|---|---|
pluginTablePrefix | 字符串 | 插件自有表/SQL 前缀建议(如 plugin_xxx_),供宿主 PluginDataAccess 白名单等使用。 |
pluginDataTables | 字符串数组 | 不加前缀 的逻辑表名列表;非空时仅允许访问与 前缀 + 表名 规则一致的 SQL(细节以宿主实现为准)。 |
dependencyFingerprintSha256 | 字符串 | 可选;JAR 内 META-INF/mms/deps-fingerprint.manifest(每行一条 groupId:artifactId:version,排序规则见 PluginConstants Javadoc)的 SHA-256,用于依赖一致性校验。 |
dependencies | 对象数组 | 依赖其它插件:每项含 id、可选 versionRange。optional 为 true 时视为 可选依赖(拓扑与缺插件校验可跳过);为 false 或未写时按 必选 处理(见宿主 PluginDependencySort)。 |
4.5 前端协作:frontend
可选;字段 modulePackage、compatibleMmsUi、routePrefixes 等与 manifests、插件市场展示相关,详见 JAR 插件路由协议。
最小常用子集(无 runtimeMode、无 frontend)仍可采用 第 3 节 示例;需要 与 plugin.example.json 逐字段对齐 时,直接对照仓库内该文件即可。
校验失败时宿主会拒绝加载;开发阶段请先对齐 revisionMin 与当前主仓 revision。
含前端元数据的 plugin.json 片段(与 JAR 插件路由协议 对齐,宿主 manifests 会带回):
{
"id": "com.example.mms.demo",
"version": "1.0.0",
"name": "演示插件",
"description": "示例",
"requiresMms": { "revisionMin": 21 },
"entryClass": "com.example.mms.demo.DemoPlugin",
"kind": "extension",
"frontend": {
"modulePackage": "@example/mms-demo-ui",
"compatibleMmsUi": "1.x",
"routePrefixes": ["/demo-plugin"]
}
}5. 功能如何封装
5.1 生命周期
实现 com.sxpcwlkj.plugin.MmsPlugin:
onLoad(PluginRuntimeContext ctx):安装就绪后调用;可读取ctx.hostMmsRevision()、ctx.pluginInstallRoot()、ctx.pluginDataDirectory()、ctx.pluginTemporaryDirectory(),做初始化(线程池、监听注册等)。onUnload():卸载或进程退出前(热卸载能力有限,生产环境见 插件宿主说明 — 能力边界)。
5.2 能否使用宿主的 Redis、数据库?
结论(当前实现):可以「连同一套基础设施」,但没有「直接用宿主已配好的 Spring Bean」的官方通道。
| 维度 | 说明 |
|---|---|
| 进程与网络 | 插件与 mms-admin 同一 JVM,访问同一台 Redis、同一数据库 IP/端口 在物理上完全可行。 |
PluginRuntimeContext | 仅提供 revision、安装目录、data/tmp 路径 等,不注入 DataSource、RedisTemplate、也不暴露 ApplicationContext。 |
| Spring 注入 | 插件入口由 ServiceLoader 实例化,不是 Spring 容器里的 Bean,@Autowired / @Resource 不会生效。 |
| 类加载 | 插件 URLClassLoader 以宿主线程的 父类加载器 为 parent,父加载器已加载的类可被插件侧引用,但需警惕 依赖版本重复(插件 fat-jar 与宿主各带一套 Lettuce/JDBC 易冲突)。 |
实务上可选做法:
插件自建连接(最常见、无需改主仓) 在
pluginDataDirectory()下或环境变量里放 独立配置(JDBC URL、Redis URI),用 JDBC、Jedis/Lettuce等自建客户端或小型连接池。注意:与宿主 连接池分离、配置重复;多租户下若写业务表须 自行拼接/传递tenant_id,否则易与宿主 MyBatis 租户插件行为不一致。宿主显式提供门面(需改主工程) 在
mms-plugin-host/ 业务模块中增加 注册表或回调,由 Spring 注入好的DataSource/StringRedisTemplate在onLoad前或之后交给插件——属 个案集成,需版本与文档同步。若希望 对所有插件稳定承诺,见下文 第 5.2.1 节(规划中的宿主能力门面)。与宿主共用 Spring / 连接池 当前宿主 不向插件注入 Spring Bean;若必须与主工程 共用
DataSource、Redis 等,须在 主仓扩展门面或回调,由集成方与项目维护者约定,而非单靠独立 JAR 自动打通。
卸载:热卸载时插件须在 onUnload() 中关闭自建连接与线程,避免泄漏。
5.2.1 「宿主能力门面」设计取向(未落地,供主仓需求评审)
现状:PluginRuntimeContext 仅 提供版本号与目录路径;没有 PluginHostServices、也没有向 SPI 注入短信 / OSS / 微信等 Bean。下述为路线图上的两种主方案,实现前以主仓 issue / 版本需求为准,与 revision 及本文档同版本强绑定。
方案 A — API 侧「窄接口」+ 宿主 Spring 实现(推荐做正式契约时)
| 要点 | 说明 |
|---|---|
| 放哪 | 在 mms-plugin-api 定义 稳定、刻意收窄 的接口(可命名为 PluginHostServices,或按域拆成 HostSmsBridge、HostOssBridge 等),只包含团队愿意长期 semver 约束的方法(例如 sendSms(…)、putObject(…)),避免把 ApplicationContext 或宽泛 DTO 漏给插件。 |
| 谁实现 | mms-plugin-host(或受控的业务模块)内由 Spring 装配真实 Bean,在 onLoad 之前 将唯一实例注册到 线程安全的通道:例如扩展 PluginRuntimeContext 增加 hostServices() / optionalHostServices(),或经过评审的 静态委托 + 生命周期钩子在卸载时清空(需杜绝插件 ClassLoader 与 Spring 双向交叉引用)。 |
| 兼容性 | 接口增方法默认视为 次版本;删/改签名视为 主版本,与 MMS **revision 对齐并在 JAR 插件入门 / 本文同步 破坏性变更说明。 |
方案 B — 轻量:宿主 HTTP 能力(不落窄接口时)
- 由
mms-admin主工程 暴露 内部 REST(或现有业务接口),插件在 SPI 内用HttpClient/RestTemplate等 带鉴权 调用(Token、内网网关、IP 白名单由运维与权限体系约束)。 - 优点:不扩
mms-plugin-apiJAR 契约即可迭代;缺点:插件需处理网络失败、幂等、鉴权轮换,且 「哪些是插件可依赖的稳定 URL」 仍建议用文档或 OpenAPI 显式列出,避免野生依赖。
能力域是否纳入正式门面(产品决策)
短信、邮箱、OSS、Redis、微信(公众号/小程序等)是否、以何种粒度 进入正式门面,应 单开需求 定一版 「宿主能力门面」 规格说明:避免一次塞满所有模块导致接口臃肿、版本锁死。未列入契约的能力,插件仍适用 第 5.2 节 的 自建连接 或 方案 B。
5.3 与 Spring / HTTP 的边界(当前阶段)
宿主 不会自动扫描插件里的 @Controller、@Service 并注册到 Spring MVC。适合封装为:
- SPI 内自管逻辑、定时任务;访问库缓存见 第 5.2 节,而非指望自动注入宿主 Bean。
- 需要对外 HTTP 的,目前应由 主工程 提供接口;插件在 SPI 内回调宿主能力若走 HTTP,见 第 5.2.1 节 · 方案 B。插件侧仅适合 SPI 内逻辑,见 插件宿主说明 — 能力边界。
5.4 可选健康检查
实现 com.sxpcwlkj.plugin.PluginHealthContributor,并增加:
META-INF/services/com.sxpcwlkj.plugin.PluginHealthContributor
宿主会聚合到 GET /system/pluginHost/health(需 super_admin)。
5.5 依赖 JAR
除 mms-plugin-api(provided)外,业务依赖若需一起加载,可将额外 JAR 放在同一版本目录的 lib/ 下(与主插件 JAR 并列),宿主会以 同一 URLClassLoader 加载(具体以当前宿主实现为准,见主仓 PluginLifecycleManager)。
6. pom.xml 最小示例(独立仓库)
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo-mms-plugin</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>21</java.version>
<mms.revision>21</mms.revision>
</properties>
<dependencies>
<dependency>
<groupId>com.sxpcwlkj</groupId>
<artifactId>mms-plugin-api</artifactId>
<version>${mms.revision}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>21</release>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>版本号 mms.revision 须与当前使用的 MMS 主工程 revision 一致。
7. 打包
主仓内(仓库根目录,替换模块名):
cd /path/to/mms
mvn -pl mms-plugins/mms-plugin-sample-health -am package -DskipTests
# 或你自己的子模块:
# mvn -pl mms-plugins/your-plugin-xxx -am package -DskipTests产物在对应模块的 target/*.jar(普通 JAR 即可,mms-plugin-api 保持 provided、勿打进 fat-jar,除非刻意处理冲突)。
仅把 API 装进本地仓库(给独立 Git 工程解析用):
cd /path/to/mms
mvn -pl mms-modules/mms-plugin-api install -DskipTests独立仓库中:上述 install 执行过一次后,在插件工程目录 mvn package 即可。
安装到宿主时,将 含 plugin.json + SPI 的业务 JAR(及可选依赖 JAR)放入约定 lib/。
7.1 与 plugin.json id/version 一致的磁盘目录示例
宿主使用 PluginInstallationLayout 将 id / version 中的 / 等字符安全化后作为目录名。若 id 为反向域名且无特殊字符,通常可理解为:
${mms.plugin.root-dir}/
com.example.mms.demo/
1.0.0/
lib/
demo-mms-plugin-1.0.0.jar
data/
tmp/手工安装时请先建好上列目录,再放入 JAR;或通过 POST /system/pluginHost/install 由宿主落盘(见 插件宿主说明)。
8. 部署到宿主
磁盘布局(mms.plugin.root-dir 可配置,根目录下):
<root-dir>/
<pluginId>/ # 与 plugin.json 的 id 经安全化后的目录名一致
<version>/ # 与 plugin.json 的 version 一致
lib/
your-plugin.jar
data/
tmp/安装方式任选:
- 手工:将
package产出的 JAR 放入上表lib/,重启或调用POST /system/pluginHost/reload(见运维文档)。 - 插件市场:超级管理员通过
POST /system/pluginHost/install上传单个 JAR(宿主写入约定目录并 reload)。
9. 自检清单
- [ ]
plugin.json中id/version/entryClass/requiresMms正确 - [ ]
META-INF/services/com.sxpcwlkj.plugin.MmsPlugin指向实现类 - [ ]
mms-plugin-api为 provided,未打进 fat-jar(除非故意 fat 且注意类冲突) - [ ] 宿主
revision与插件requiresMms匹配 - [ ] 目录层级为
<root>/<pluginId>/<version>/lib/*.jar
10. 相关链接
- JAR 插件入门 — 配置、目录、HTTP、能力边界
- JAR 插件路由协议 —
frontend、manifest、菜单与市场 - 项目简介 — 子模块速览 —
mms-plugin-api/mms-plugin-host等 - 主仓示例:
mms-plugins/mms-plugin-sample-health - 契约示例:
mms-plugin-api→META-INF/mms/plugin.example.json
