Skip to content

JAR 开发指南

面向 第三方或内部团队:在 不改动 MMS 主工程源码 的前提下,编写独立 Maven 工程,产出符合契约的 JAR,由宿主(mms-plugin-host)扫描加载。第一次从 0 跑通示例,请先读 第 0 节(阅读画像、术语、mms-plugin-api 安装、闭环步骤与排障)。

运维配置与 HTTP 接口见 JAR 插件入门;管理端前端元数据与路由约定见 JAR 插件路由协议

新手推荐阅读顺序(按顺序翻页即可)

  1. 0. 第一次写插件:先知道自己要会什么、术语一遍过、mms-plugin-api 安装、10 分钟闭环、排障表。
  2. JAR 插件入门:宿主 mms.plugin 配置、磁盘目录、/system/pluginHost/*、管理端菜单一览。
  3. 本文第 1~10 节:前置条件、工程形态、plugin.json / SPI、Redis/库与「宿主能力门面」规划(第 5.2~5.2.1 节)、打包与自检清单。
  4. 进阶(有可独立发布的前端时再读)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.jsonentryClass 不一致

  • JAR 内必备资源(最小集)

    1. META-INF/mms/plugin.json(或用兼容路径 plugin.json,推荐前者)
    2. 上述 MmsPlugin 的 SPI 文件
    3. entryClass 指向的 .class 可选:实现 PluginHealthContributor 时再加对应 META-INF/services/...PluginHealthContributor 文件。

0.3 mms-plugin-api 不在 Maven 中央仓:install 保姆步骤

独立 Git 仓库写插件时,IDE 会报 找不到 com.sxpcwlkj:mms-plugin-api不是 你网络问题,而是 该坐标未发布到中央仓库

已克隆的 MMS 主仓根目录执行(只需在你本机成功执行 一次;换版本升级主仓后若 revision 变了,可再执行):

bash
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 完成安装与验证。

① 克隆主仓、确认版本

bash
git clone <你的 mms 远程地> mms && cd mms

打开 仓库根 pom.xml,记下 revision(文档编写时常见为 21,以你克隆到的仓库为准)。后面 plugin.jsonrequiresMms.revisionMinmms.plugin.host-mms-revision 都要与当前主工程版本 对齐

② 打开宿主开关(application.yml

mms-admin 使用的配置里确认存在 (或与下文等价的 dev/profile 合并结果)

yaml
mms:
  plugin:
    enabled: true
    root-dir: ${user.home}/mms/plugins
    host-mms-revision: ${revision}

含义简述:root-dir 下将按 pluginId / version / lib 落盘;host-mms-revision 须与根 revision 一致。详见 JAR 插件入门

③ 只编示例插件(不改代码也能通)

bash
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.jsonidmms.plugin.sample-healthversion1.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/statusplugins 列表中应出现示例插件;pluginsRootReadytrue
  • GET /system/pluginHost/health:应能看到示例插件返回的 JSON 片段(示例工程实现了 PluginHealthContributor)。

若此处仍为空,先看 status.resolvedPluginsRoot 与你手放 JAR 的路径是否一致,再查应用日志里 PluginLifecycleManager / PluginException 的报错。

0.5 常见问题速查

现象建议
revision / requiresMms 校验失败pomrevisionmms.plugin.host-mms-revisionplugin.jsonrevisionMin/revisionMax 三者对齐;改完重新打包插件或更新磁盘上的 plugin.json(以你当前流程为准)。
报 SPI 未发现、entryClass 找不到检查 META-INF/services/com.sxpcwlkj.plugin.MmsPlugin 是否 恰好一行、无 BOM、类名与 entryClass 一致;JAR 里是否真有 META-INF/mms/plugin.json
pluginsRootReady: falseroot-dir 所在盘符无写权限、路径被安全软件拦截,或配置未加载到当前 profile。
改完代码却始终加载旧逻辑确认替换的是 对应 pluginId/versionlib 的 JAR;需要 reload 或重启;热替换在能力边界内 不保证 与重启等价。
独立仓库仍解析不了 API确认已执行 第 0.3 节install,且插件 pommms-plugin-api 版本 = 主仓 revision

0.6 下一步:菜单与带界面的插件(第二步)


1. 前置条件

说明
JDK21(与主工程 java.version 一致)
宿主mms.plugin.enabled=true,且 mms.plugin.host-mms-revision 与根 pomrevision(如 21)一致
依赖坐标com.sxpcwlkj:mms-plugin-apiprovided 作用域,版本与主工程 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 解耦:

text
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-apiprovided);并在 mms-plugins/pom.xml<modules> 中登记该模块。

在聚合工程中登记新子模块mms-plugins/pom.xml):

xml
<modules>
    <module>mms-plugin-sample-health</module>
    <module>your-plugin-xxx</module>
</modules>

子模块 pom.xml 演示(与 mms-plugin-sample-health 同款结构):

xml
<?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)。

text
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(在主仓根目录执行一次即可):

bash
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,或使用主仓:

bash
cd /path/to/mms
mvn -pl mms-plugins/your-plugin-xxx -am package -DskipTests

2.3 打包与交付形态(瘦 JAR / standalone)

与「工程放哪」正交:同一套 plugin.json + SPI,产物可以是 瘦包fat/standalone,由 依赖是否必须跟插件进 lib 决定。

交付形态典型场景说明
瘦 JAR仅依赖 mms-plugin-api(provided) 与宿主已有类库能覆盖的运行时mvn package 默认瘦包即可;同一 pluginId/versionlib/ 下可并列多个 JAR,宿主同一 ClassLoader 加载(见 第 5.5 节)。
standalone(shade 等)需自带 DeepL SDK、整站 doc 业务 等宿主未提供的传递依赖使用 maven-shade-plugin 等打出 *-standalone.jar;上传 install 往往只收 一个文件,须把依赖打进去,否则会 NoClassDefFoundError。详见 DeepL文档站合并 等分册。

2.4 运行与扩展类型(便于选型)

维度可选值 / 说明
plugin.json · runtimeModeSPI_ONLY(未写该字段时 默认,兼容旧插件):仅 MmsPlugin SPI,向宿主 Web 端口挂业务路由。 HOST_MVC:可向宿主主端口暴露 /plugin/{pluginId}/... 形态接口(示例见 示例健康插件)。 INDEPENDENT_PROCESS:契约内预留 独立进程 + 端口;当前宿主侧以 元数据与 SPI 为主,是否起独立进程以主仓实现与版本说明为准,勿仅按字段名假设行为。
kindextension(默认):业务扩展,一般有 entryClass + SPI。 library:给其他插件依赖的库包;可无 entryClass / 入口 SPI,由宿主依赖图解析(见 mms-plugin-api PluginKind)。
HostServices 对齐hostServicesContractVersion:未写则按 1 处理;使用 PluginRuntimeContext 上缓存等宿主能力 时,版本须与宿主契约一致(分册里常见为 13,以各插件为准)。

更多 权限码、HOST_MVC 路径、invoke 等运维向约定见 JAR 插件分册总览

3. 源码与 JAR 内目录结构

最小可加载布局示例:

text
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.MmsPlugin
  • plugin.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 展示完整字段(含 requiresMmsfrontenddependencies);最小示例如 mms-plugins/mms-plugin-sample-healthplugin.json

最小可加载 plugin.json(与示例工程一致,可按需改名):

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 一致):

text
com.example.mms.demo.DemoPlugin

入口类演示(实现 MmsPlugin;可选再实现 PluginHealthContributor 并单独注册对应 services 文件):

java
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):

text
com.example.mms.demo.DemoPlugin

4. plugin.json 描述文件(格式与字段)

宿主读取 META-INF/mms/plugin.json(或兼容 plugin.json,见 JAR 插件入门)。JSON 根对象与主仓 PluginDescriptor 一一对应;未知字段默认忽略(便于向前兼容)。权威示例mms-plugin-apiMETA-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 不是 Maven groupId:artifactId;artifact 名可变,id 一经上线尽量稳定
  • 升版通常只改 version,并在宿主 lib/ 下使用 新版本目录;与 requiresMmsrevisionMin 联动见 第 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

仅当 runtimeModeINDEPENDENT_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、可选 versionRangeoptionaltrue 时视为 可选依赖(拓扑与缺插件校验可跳过);为 false 或未写时按 必选 处理(见宿主 PluginDependencySort)。

4.5 前端协作:frontend

可选;字段 modulePackagecompatibleMmsUiroutePrefixes 等与 manifests、插件市场展示相关,详见 JAR 插件路由协议


最小常用子集(无 runtimeMode、无 frontend)仍可采用 第 3 节 示例;需要 plugin.example.json 逐字段对齐 时,直接对照仓库内该文件即可。

校验失败时宿主会拒绝加载;开发阶段请先对齐 revisionMin 与当前主仓 revision

含前端元数据的 plugin.json 片段(与 JAR 插件路由协议 对齐,宿主 manifests 会带回):

json
{
  "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 路径 等,注入 DataSourceRedisTemplate、也不暴露 ApplicationContext
Spring 注入插件入口由 ServiceLoader 实例化不是 Spring 容器里的 Bean,@Autowired / @Resource 不会生效
类加载插件 URLClassLoader 以宿主线程的 父类加载器 为 parent,父加载器已加载的类可被插件侧引用,但需警惕 依赖版本重复(插件 fat-jar 与宿主各带一套 Lettuce/JDBC 易冲突)。

实务上可选做法:

  1. 插件自建连接(最常见、无需改主仓)pluginDataDirectory() 下或环境变量里放 独立配置(JDBC URL、Redis URI),用 JDBC、Jedis/Lettuce 等自建客户端或小型连接池。注意:与宿主 连接池分离、配置重复;多租户下若写业务表须 自行拼接/传递 tenant_id,否则易与宿主 MyBatis 租户插件行为不一致。

  2. 宿主显式提供门面(需改主工程)mms-plugin-host / 业务模块中增加 注册表或回调,由 Spring 注入好的 DataSource / StringRedisTemplateonLoad 前或之后交给插件——属 个案集成,需版本与文档同步。若希望 对所有插件稳定承诺,见下文 第 5.2.1 节(规划中的宿主能力门面)

  3. 与宿主共用 Spring / 连接池 当前宿主 不向插件注入 Spring Bean;若必须与主工程 共用 DataSource、Redis 等,须在 主仓扩展门面或回调,由集成方与项目维护者约定,而非单靠独立 JAR 自动打通。

卸载:热卸载时插件须在 onUnload() 中关闭自建连接与线程,避免泄漏。

5.2.1 「宿主能力门面」设计取向(未落地,供主仓需求评审)

现状PluginRuntimeContext 提供版本号与目录路径;没有 PluginHostServices、也没有向 SPI 注入短信 / OSS / 微信等 Bean。下述为路线图上的两种主方案,实现前以主仓 issue / 版本需求为准,revision 及本文档同版本强绑定

方案 A — API 侧「窄接口」+ 宿主 Spring 实现(推荐做正式契约时)

要点说明
放哪mms-plugin-api 定义 稳定、刻意收窄 的接口(可命名为 PluginHostServices,或按域拆成 HostSmsBridgeHostOssBridge 等),只包含团队愿意长期 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-api JAR 契约即可迭代;缺点:插件需处理网络失败、幂等、鉴权轮换,且 「哪些是插件可依赖的稳定 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 最小示例(独立仓库)

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. 打包

主仓内(仓库根目录,替换模块名):

bash
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 工程解析用):

bash
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 一致的磁盘目录示例

宿主使用 PluginInstallationLayoutid / version 中的 / 等字符安全化后作为目录名。若 id 为反向域名且无特殊字符,通常可理解为:

text
${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 可配置,根目录下):

text
<root-dir>/
  <pluginId>/          # 与 plugin.json 的 id 经安全化后的目录名一致
    <version>/         # 与 plugin.json 的 version 一致
      lib/
        your-plugin.jar
      data/
      tmp/

安装方式任选:

  1. 手工:将 package 产出的 JAR 放入上表 lib/,重启或调用 POST /system/pluginHost/reload(见运维文档)。
  2. 插件市场:超级管理员通过 POST /system/pluginHost/install 上传单个 JAR(宿主写入约定目录并 reload)。

9. 自检清单

  • [ ] plugin.jsonid / version / entryClass / requiresMms 正确
  • [ ] META-INF/services/com.sxpcwlkj.plugin.MmsPlugin 指向实现类
  • [ ] mms-plugin-apiprovided,未打进 fat-jar(除非故意 fat 且注意类冲突)
  • [ ] 宿主 revision 与插件 requiresMms 匹配
  • [ ] 目录层级为 <root>/<pluginId>/<version>/lib/*.jar

10. 相关链接

Released under the MIT License.