所有的代码生成器都是基于模板+数据的形式来进行生成,类似于Vue在死的页面中注入动态的数据。
在 RuoYi 框架中,这一过程由Velocity 模板引擎驱动实现。框架内置了完整的前后端代码模板,这些模板文件集中存放在项目的模板目录中,开发者可以通过修改模板来定制生成代码的格式、结构和业务逻辑,从而满足不同项目的个性化需求。
Velocity 模板引擎在 RuoYi 中的应用
Velocity 是一款基于 Java 的模板引擎,它允许开发者通过模板文件定义输出格式,再将动态数据注入模板中生成最终内容。在 RuoYi 的代码生成模块中,Velocity 的使用主要分为两个关键步骤:
1. 初始化 Velocity 引擎
//初始化 Velocity 模板引擎
VelocityInitializer.initVelocity();
2. 准备模板渲染上下文
//准备 Velocity 模板渲染所需的上下文对象,可以理解为将数据注入到模板中
VelocityContext context = VelocityUtils.prepareContext(table);
VelocityContext相当于一个数据容器,用于存储所有需要传递给模板的变量。在VelocityUtils.prepareContext(table)方法中,RuoYi 会将表结构信息(table对象)转换为模板可识别的变量,主要包括:
- 表基本信息:表名、类名、注释、作者等
- 字段信息:字段名、Java 属性名、数据类型、注释、是否主键等
- 生成配置:包路径、模块名、业务名等
- 工具类:字符串处理工具、日期工具等
RuoYi 中常用的 Velocity 模板语法
Velocity 模板使用简洁的语法实现数据注入和逻辑控制,以下是 RuoYi 代码生成中最常用的语法规则:
1. 变量引用
使用${变量名}引用上下文对象中的变量,例如:
// 引用类名
public class ${className}Controller { ... }
// 引用字段注释
/**
* ${column.comment}
*/
private ${column.javaType} ${column.javaField};
2. 条件判断
使用#if、#elseif、#else、#end实现条件判断,例如:
#if(${column.isPrimaryKey})
// 主键字段处理逻辑
@TableId
#elseif(${column.isAutoIncrement})
// 自增字段处理逻辑
@TableField(fill = FieldFill.INSERT)
#else
// 普通字段处理逻辑
@TableField
#end
private ${column.javaType} ${column.javaField};
3. 循环遍历
使用#foreach、#end遍历集合,例如:
// 遍历所有字段生成getter方法
#foreach($column in $columns)
public ${column.javaType} get${column.javaField?cap_first}() {
return ${column.javaField};
}
#end
这里只对ruoyi中常用的Velocity方法讲解,详情讲解了 Velocity 的规则如何配置。
代码生成流程
功能使用

执行流程

代码实现
步骤 1:接收请求,触发代码生成
前端点击 "下载" 后,请求后端/download/{tableName}接口,接口通过权限校验后,调用服务层生成代码:
/**
* 生成代码(下载方式)
*/
@PreAuthorize("@ss.hasPermi('tool:gen:code')")
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
@GetMapping("/download/{tableName}")
public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException
{
byte[] data = genTableService.downloadCode(tableName);
//返回类型为zip压缩文件
genCode(response, data);
}
步骤 2:创建内存流,准备压缩环境
服务层使用ByteArrayOutputStream在内存中临时存储压缩文件,避免写入磁盘带来的 IO 开销:
/**
* 生成代码(下载方式)
*
* @param tableName 表名称
* @return 数据
*/
@Override
public byte[] downloadCode(String tableName)
{
//内存中临时存储压缩文件的内容,避免直接写入磁盘
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//包装字节数组输出流,提供 ZIP 压缩功能
ZipOutputStream zip = new ZipOutputStream(outputStream);
//根据表名生成具体的代码文件,并将这些文件写入 ZIP 输出流中
generatorCode(tableName, zip);
//使用 Apache Commons IO 工具类的 closeQuietly 方法安全关闭流
IOUtils.closeQuietly(zip);
//内存中的 ZIP 文件内容转换为字节数组返回
return outputStream.toByteArray();
}
步骤 3:查询表信息,完善数据
从数据库查询表结构详情,并补充主子表、主键等关键信息,为后续数据注入做准备:
/**
* 查询表信息并生成代码
*/
private void generatorCode(String tableName, ZipOutputStream zip)
{
// 查询表信息
GenTable table = genTableMapper.selectGenTableByName(tableName);
// 设置主子表信息
setSubTable(table);
// 设置主键列信息
setPkColumn(table);
....
}
步骤 4:Velocity 渲染,生成代码字符串
初始化 Velocity 引擎和上下文,加载模板并渲染,将 "模板 + 数据" 转换为代码字符串:
// 4. 初始化Velocity引擎
VelocityInitializer.initVelocity();
// 5. 构建上下文,注入表信息
VelocityContext context = VelocityUtils.prepareContext(table);
// 6. 获取模板列表(如Controller、Service、Entity、Vue等)
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
// 遍历模板,逐个渲染并写入ZIP
for (String template : templates) {
// 7. 字符流:缓存渲染后的代码
StringWriter sw = new StringWriter();
// 8. 加载模板(指定UTF-8编码)
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
// 9. 渲染模板:将上下文数据注入模板,结果写入字符流
tpl.merge(context, sw);
// ...后续步骤:写入ZIP包...
}
步骤 5:写入 ZIP 包,完成生成
将渲染后的代码字符串写入 ZIP 输出流,每个模板对应 ZIP 中的一个文件:
try {
// 10. 创建ZIP条目(即压缩包中的一个文件,如UserController.java)
String fileName = VelocityUtils.getFileName(template, table);
zip.putNextEntry(new ZipEntry(fileName));
// 11. 将代码字符串写入ZIP条目
IOUtils.write(sw.toString(), zip, Constants.UTF8);
// 12. 清理资源:关闭字符流、刷新ZIP流、关闭当前条目
IOUtils.closeQuietly(sw);
zip.flush();
zip.closeEntry();
} catch (IOException e) {
log.error("渲染模板失败,表名:" + table.getTableName(), e);
}
RuoYi Velocity 模板引擎解析
1万+

被折叠的 条评论
为什么被折叠?



