4、第一个Hello World插件:创建Maven模块、实现基础接口、打包与部署

好,咱们正式开始动手了。

前面几章讲了插件架构、核心接口,说实话都是理论。这一章,咱们真刀真枪地写一个插件出来。我管它叫 HelloWorldPlugin,功能很简单——收到设备上报的数据后,在日志里打印一句 "Hello World"。

别小看这个例子。它麻雀虽小,五脏俱全。你跑通了这个流程,后面写任何业务插件都只是换逻辑而已。

4.1 创建Maven模块

ThingsBoard 本身是用 Maven 管理的多模块项目。我个人习惯,插件代码不要直接塞进主工程里,而是单独建一个模块,方便后续独立维护和升级。

在 ThingsBoard 源码根目录下,创建一个新的 Maven 模块:

thingsboard/
├── application/
├── common/
├── dao/
├── ...
└── extensions/          ← 新建这个目录
    └── hello-world/     ← 我们的插件模块
        └── pom.xml

pom.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>org.thingsboard</groupId>
        <artifactId>thingsboard</artifactId>
        <version>3.5.0</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>

    <artifactId>hello-world-plugin</artifactId>
    <packaging>jar</packaging>
    <name>Hello World Plugin</name>

    <dependencies>
        <dependency>
            <groupId>org.thingsboard</groupId>
            <artifactId>rule-engine-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.thingsboard</groupId>
            <artifactId>common</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
小提示: scope 设为 provided 是因为这些依赖在 ThingsBoard 运行时已经存在,打包时不需要重复包含。我第一次写插件时没注意这个,结果打出来的 jar 包 50 多 MB,部署上去直接冲突了。

4.2 实现基础接口

插件要干活,至少得实现两个接口:RuleNodeRuleNodeConfiguration。说白了,一个告诉框架「我是什么」,一个告诉框架「我怎么配置」。

先写节点类:

@RuleNode(
    name = "Hello World",
    description = "打印 Hello World 日志",
    nodeDescription = "收到消息后打印一条问候语",
    configClazz = HelloWorldConfiguration.class,
    relationTypes = {"Success", "Failure"}
)
public class HelloWorldNode implements RuleNode {

    @Override
    public void init(RuleNodeConfiguration configuration) {
        // 初始化逻辑,比如读取配置
        System.out.println("HelloWorldNode 初始化完成");
    }

    @Override
    public void onMsg(RuleContext ctx, RuleNodeMsg msg) {
        try {
            // 核心逻辑:打印 Hello World
            String deviceName = msg.getMetadata().getValue("deviceName");
            System.out.println("Hello World from device: " + deviceName);

            // 告诉框架,处理成功
            ctx.tellSuccess(msg);
        } catch (Exception e) {
            // 出错了,走 Failure 分支
            ctx.tellFailure(msg, e);
        }
    }

    @Override
    public void destroy() {
        // 清理资源
        System.out.println("HelloWorldNode 销毁");
    }
}

再写配置类:

public class HelloWorldConfiguration implements RuleNodeConfiguration {

    private String greeting = "Hello";

    public String getGreeting() {
        return greeting;
    }

    public void setGreeting(String greeting) {
        this.greeting = greeting;
    }
}
重点: onMsg 方法里一定要调用 ctx.tellSuccessctx.tellFailure。我曾经见过有人忘了调,结果消息卡在节点里,整个规则链都堵死了。

4.3 打包与部署

代码写完了,接下来就是打包。在 hello-world 目录下执行:

mvn clean package -DskipTests

打包完成后,在 target/ 目录下会生成 hello-world-plugin-3.5.0.jar

部署分两步:

  1. 复制 jar 包 到 ThingsBoard 的 extensions/ 目录下。
  2. 重启 ThingsBoard 服务,让框架加载新插件。

重启后,在规则链编辑器中,你应该能在节点列表里找到「Hello World」这个节点。拖出来,连上线,保存,然后发一条设备数据试试。

注意: 如果重启后找不到节点,先检查日志。常见问题有两个:一是 jar 包版本和 ThingsBoard 主版本不匹配,二是 @RuleNode 注解里的 configClazz 写错了类名。这两个坑我都踩过,排查起来很费时间。

4.4 验证与调试

部署成功后,打开 ThingsBoard 的日志文件(一般在 logs/thingsboard.log),你应该能看到类似这样的输出:

2024-01-15 10:23:45.123  INFO [rule-engine] HelloWorldNode 初始化完成
2024-01-15 10:23:45.456  INFO [rule-engine] Hello World from device: 温度传感器A

如果没看到,别慌。先确认规则链是否真的触发了,再检查设备数据是否到达了该节点。我一般会在 onMsg 方法里加一行 System.out.println("收到消息: " + msg.getData()),先确认消息进来了,再排查后续逻辑。

步骤 操作 预期结果
1 创建 Maven 模块 项目结构完整,pom.xml 无报错
2 实现 RuleNode 接口 编译通过,无语法错误
3 打包 生成 jar 包,大小在 10KB 左右
4 部署并重启 日志显示初始化成功
5 发送测试数据 日志打印 Hello World

嗯,到这里,你的第一个 ThingsBoard 插件就跑通了。虽然功能简单,但整个流程——从创建模块、实现接口、打包部署到验证——你已经完整走了一遍。后面再写复杂的插件,无非是多加几个配置项、多处理几种消息类型而已。

下一章,我会带你写一个真正能用的插件:从 MQTT 主题中提取数据,转换成 ThingsBoard 能识别的格式。到时候你会看到,插件开发的核心其实就两件事——接住消息,然后处理它