模块深度解析 · 代码生成器(ruoyi-generator)
📍 核心源码在
ruoyi-generator/src/main/java/com/ruoyi/generator/下的util/、service/、domain/,模板在src/main/resources/vm/。
1. 模块概述
代码生成器让开发者”根据数据库表一次生成整套前后端 CRUD 代码”。这是若依生产效率的核心卖点——从建表到可运行功能,把重复劳动降到最低。
生成的产物包括:
| 产物 | 模板 | 示例路径 |
|---|---|---|
| 领域实体 | vm/java/domain.java.vm |
com/xx/xx/domain/Test.java |
| Mapper 接口 | vm/java/mapper.java.vm |
.../mapper/TestMapper.java |
| Service 接口+实现 | vm/java/service*.vm |
ITestService / TestServiceImpl |
| Controller | vm/java/controller.java.vm |
TestController |
| XML 映射 | vm/xml/mapper.xml.vm |
mapper/system/TestMapper.xml |
| Vue 页面 | vm/vue/**/index.vue.vm |
列表页 |
| 菜单 SQL | vm/sql/sql.vm |
businessNameMenu.sql |
三种生成类型 tplCategory:CRUD(单表)、树表(index-tree.vue)、主子表(sub-domain.java)。前端可选 element-plus / element-plus-typescript 模板。
graph LR
DB[(数据库表)] --> GenTableServiceImpl
GenTableServiceImpl --> GenUtils[initTable/initColumnField
表→元数据对象]
GenTableServiceImpl --> VelocityUtils[prepareContext
填充模板变量]
VelocityUtils --> VELOCITY[Velocity 引擎]
VELOCITY --> FILES[生成 java/vue/xml/sql 文件或zip]
2. 核心概念:表 → 元数据 → 模板 → 文件
流程可拆为四步:
- 读取表结构:
GenTableServiceImpl扫描information_schema(或用selectTableByName)得到表与列元数据 →GenTable/GenTableColumn。 - 初始化语义(
GenUtils.initTable/initColumnField):把数据库类型(varchar/int)推断成 Java 类型(String/Integer/Long/BigDecimal/Date),决定字段是否可编辑/可查询/是否下拉。 - 准备模板上下文(
VelocityUtils.prepareContext):把类名、包名、列列表、字典、树/子表配置填入VelocityContext。 - 选择模板渲染(
VelocityUtils.getTemplateList / getFileName):按类型选.vm,渲染到目标路径或打 zip 下载。
sequenceDiagram
participant C as GenController
participant S as GenTableServiceImpl
participant G as GenUtils
participant V as VelocityUtils
C->>S: importTable 导入选中的表
S->>S: 查表/列元数据 → GenTable/GenTableColumn
S->>G: initTable / initColumnField
S->>S: 保存 gen_table/gen_table_column
C->>S: generatorCode(tableName)
S->>V: prepareContext + getTemplateList
V->>V: Velocity 渲染每个模板
S-->>C: 返回 zip / 落盘 / 写 menu.sql
3. API Signatures
1 | public class GenUtils |
1 | public class VelocityUtils |
1 | public class VelocityInitializer |
4. 数据结构深度解析
GenTable(生成表元数据)
字段:tableId tableName tableComment className packageName moduleName businessName functionName functionAuthor tplCategory tplWebType options columns(PK/父表/子表...) lostPtr。
initColumnField 的关键推断规则(GenUtils:47-79)
| 数据库类型 | 推断 Java 类型 | 前端控件 |
|---|---|---|
| char/varchar/text | String(text≥500 或 longtext → textarea) | input / textarea |
| date/datetime/timestamp | Date | datetime |
| int(≤10) / 长整数 | Integer / Long | input |
| decimal(x,y)(y>0) | BigDecimal | input |
字段名后缀也决定控件:name→like 查询、status→radio、type/sex→select、image→图片上传、file→文件、content→富文本(GenUtils:100-130)。
5. 函数逐行精讲
5.a + 5.b:GenUtils.initColumnField(列语义推断核心)
函数:
initColumnField
- 调用时机:导入表时逐列调用
- 目的:由数据库列推断 Java 类型与前端控件
1 | public static void initColumnField(GenTableColumn column, GenTable table) |
📍 源码:GenUtils.java:35-131
5.a + 5.b:VelocityUtils.prepareContext(模板变量组装)
函数:
prepareContext
- 调用时机:渲染任一模板前
- 目的:把所有模板所需变量塞进
VelocityContext
1 | public static VelocityContext prepareContext(GenTable genTable) |
6. 关键算法剖析:模板→文件名映射
getFileName(template, genTable) 根据模板文件名关键词分发到对应输出路径(VelocityUtils.java:199-277):
| 模板关键词 | 输出 |
|---|---|
domain.java.vm |
javaPath/domain/ClassName.java |
mapper.java.vm |
.../mapper/ClassNameMapper.java |
service.java.vm |
.../service/IClassNameService.java |
serviceImpl.java.vm |
.../service/impl/ClassNameServiceImpl.java |
controller.java.vm |
.../controller/ClassNameController.java |
mapper.xml.vm |
mapper/module/ClassNameMapper.xml |
sql.vm |
businessNameMenu.sql |
index.vue.vm |
vue/views/module/business/index.vue |
javaPath = main/java/<package以.转/>,mybatisPath = main/resources/mapper/<module>。
7. 设计决策分析
7.1 为什么用模板引擎(Velocity)而非手写拼接?
模板引擎把”代码的结构”和”注入的数据”分离:.vm 文件可读性强、易维护,支持条件判断(#if/#foreach)与循环,比字符串拼接清晰得多。若依同时支持 Vue2/Vue3/TS 多套模板,靠 tplWebType 选择,极大提升了复用性。
7.2 为什么表结构语义要”推断”而非让用户逐字段配置?
简称(GenUtils 自动推断)降低了使用门槛:开发者只需把表建好,生成器自动推断字段的 Java 类型、是否可查询、前端控件。真正的自定义项(如字典、树字段)通过 options JSON 在生成界面配置,兼顾自动化与灵活性。
8. 学习检查点
📝 本章小结
- 代码生成 = 表结构元数据(
GenTable/GenTableColumn)→ 语义推断(GenUtils)→ 模板上下文(VelocityUtils)→ Velocity 渲染。 initColumnField用一套规则把 DB 类型映射成 Java 类型与前端控件,降低配置成本。- 三套生成形态:单表 CRUD / 树表 / 主子表;前端多模板(Vue3 / Vue3-TS)。
- 输出含 java、xml、vue、js/ts、menu.sql,可直接落地或打包下载。
🤔 思考题
getBusinessName取”最后一个下划线后的部分”,对表名sys_user与user_role会分别得到什么?有何隐患?参考答案
sys_user→user,user_role→role。隐患是业务名可能过于笼统(sys_user和user_role都得到通用词),且如果表名不含下划线会得到整个表名。所以生成器也支持在界面手动改businessName/className(GenUtils.java:164)。initColumnField对decimal(10,2)和int(11)分别推断哪种 Java 类型?为什么?参考答案
decimal(10,2)小数位>0 →BigDecimal(保证精度,避免浮点误差);int(11)长度>10 →Long,≤10 →Integer(GenUtils.java:59-79)。这是”数值类型按宽度与精度分级”的典型策略。