动词算子式通用代码生成器"光船"(Lightboat)近日将全部插件源码公开到 Gitee,包括蛋糕商城插件 CookieShopPlugin 和扩展工具插件 ExtendedUtilPlugin。对于一直在用光船做项目生成、却对"客户化动词"只停留在配置层面的开发者来说,这次开放等于拿到了完整的内部图纸——从动词定义、模板渲染到最终产物拼装,每一环都可以直接阅读和改造。
动词算子式:不只是"模板填空"
传统代码生成器的思路是"模板 + 变量替换",输出固定结构的文件。光船的动词算子式设计把生成逻辑拆成一个个"动词"(Verb),每个动词对应一类操作——比如 Create、Read、Update、Delete,或者更业务化的 Shop、Cart、Checkout。动词内部再组合"算子"(Operator)来完成具体的代码拼装步骤。
这种拆法的好处是:新增业务场景时,你不需要重写整块模板,只需要定义一个新动词,或者给已有动词挂一个新算子。插件就是承载这些自定义动词和算子的容器。
两个插件源码:从商城到工具链
CookieShopPlugin——蛋糕商城的全套动词
CookieShopPlugin 是一个完整的电商场景插件,覆盖了从商品展示到下单结算的常见动词。阅读它的源码,你能看到:
- 动词注册机制:每个动词如何声明自己的名称、入参结构和输出目标。
- 算子编排顺序:一个动词内部多个算子的执行链是怎么串联的,哪些算子负责生成 DAO,哪些负责生成 Controller。
- 模板与占位符约定:光船模板文件里
{{fieldName}}、{{verbName}}这类占位符的实际用法,以及模板文件在插件中的存放路径约定。
ExtendedUtilPlugin——通用工具算子扩展
ExtendedUtilPlugin 则偏底层,提供的是跨场景复用的工具类算子,比如:
- 通用校验算子(字段非空、格式正则)
- 通用转换算子(日期格式化、枚举映射)
- 输出目录清理与合并算子
这类插件的价值在于:当你写自己的业务插件时,可以直接引用 ExtendedUtilPlugin 里的算子,不用从零实现基础逻辑。
实践:克隆源码并跑一个最小定制动词
下面用一个可复制的流程,把两个插件源码拉到本地,阅读结构,然后基于 ExtendedUtilPlugin 写一个最小自定义动词。
第一步:克隆插件仓库
# 克隆蛋糕商城插件
git clone https://gitee.com/jerryshensjf/CookieShopPlugin.git
# 克隆扩展工具插件
git clone https://gitee.com/jerryshensjf/ExtendedUtilPlugin.git
# 查看插件目录结构
tree CookieShopPlugin -L 2
tree ExtendedUtilPlugin -L 2
克隆完成后,重点关注每个插件根目录下的 verb 子目录——那里是动词定义的核心文件。算子文件通常在 operator 子目录中,模板在 template 子目录中。
第二步:阅读一个现有动词的定义
打开 CookieShopPlugin 里的任意一个动词定义文件(假设路径为 verb/CreateOrderVerb.json),你会看到类似这样的结构:
{
"verbName": "CreateOrder",
"description": "生成订单创建接口及数据访问层",
"inputFields": [
{ "name": "orderId", "type": "String", "required": true },
{ "name": "customerName", "type": "String", "required": true },
{ "name": "totalAmount", "type": "Decimal", "required": false }
],
"operators": [
"ValidateRequiredOperator",
"GenerateDAOOperator",
"GenerateControllerOperator",
"MergeOutputOperator"
],
"outputTarget": {
"language": "java",
"framework": "springboot",
"basePath": "{{projectName}}/src/main/java"
}
}
关键字段一目了然:verbName 是动词标识,operators 列出算子执行链,outputTarget 声明输出语言和路径模板。
第三步:写一个最小自定义动词
基于 ExtendedUtilPlugin 的算子,定义一个只做字段校验和 Markdown 文档生成的轻量动词。在 ExtendedUtilPlugin 的 verb 目录下新建 DescribeFeatureVerb.json:
{
"verbName": "DescribeFeature",
"description": "根据输入字段列表生成功能描述 Markdown 文档",
"inputFields": [
{ "name": "featureTitle", "type": "String", "required": true },
{ "name": "fieldList", "type": "List<FieldDef>", "required": true }
],
"operators": [
"ValidateRequiredOperator",
"GenerateMarkdownOperator"
],
"outputTarget": {
"language": "markdown",
"framework": "none",
"basePath": "{{projectName}}/docs"
}
}
然后在 template 目录下新建对应的模板文件 DescribeFeature.md.tpl:
# {{featureTitle}}
## 字段说明
| 字段名 | 类型 | 必填 |
|--------|------|------|
{{#each fieldList}}
| {{this.name}} | {{this.type}} | {{#if this.required}}是{{else}}否{{/if}} |
{{/each}}
这个动词只调用了两个算子:ValidateRequiredOperator(来自 ExtendedUtilPlugin,校验 featureTitle 和 fieldList 不为空)和 GenerateMarkdownOperator(渲染上面的模板)。改动量极小,但完整展示了"定义动词 → 选算子 → 写模板"的定制链路。
第四步:在光船中注册并运行
假设光船的主程序已经安装,在光船的插件配置文件(通常是 plugins.json 或 lightboat.yaml)中追加本地插件路径:
plugins:
- path: ./ExtendedUtilPlugin
enabled: true
- path: ./CookieShopPlugin
enabled: true
然后执行生成命令:
# 运行自定义动词
lightboat run --verb DescribeFeature \
--input '{"featureTitle":"用户注册","fieldList":[{"name":"username","type":"String","required":true},{"name":"email","type":"String","required":true},{"name":"age","type":"Int","required":false}]}' \
--project my-app
输出会在 my-app/docs/ 下生成一份 用户注册.md,内容就是上面模板渲染的结果。
拿到源码之后该做什么
源码开放的最大价值不是"能跑了",而是"能改了"。几个建议方向:
- 先读再改:把 CookieShopPlugin 的一个完整动词从定义到模板到算子串联读一遍,理解全链路后再动手定制。
- 复用算子优先:业务动词尽量引用 ExtendedUtilPlugin 中已有的校验、转换、合并算子,减少重复实现。
- 小动词验证:新插件先写一个只含 1-2 个算子的最小动词,确认注册和渲染链路通畅后再逐步丰富。
- 注意输出路径冲突:多个动词如果
basePath模板相同,合并算子会覆盖文件,务必在动词定义中区分路径或使用文件名模板变量。
光船的动词算子式架构把"生成什么"和"怎么生成"解耦到了动词和算子两个层级。插件源码的开放让这两层都变得透明可改——从阅读 CookieShopPlugin 的电商动词开始,到用 ExtendedUtilPlugin 的算子拼出自己的最小动词,整个链路都可以在本地完成。如果你正在做项目脚手架或内部代码生成工具,这套插件结构值得直接参考和移植。