RuoYi 代码生成器详解:Velocity 模板引擎的实践与应用

RuoYi Velocity 模板引擎解析

所有的代码生成器都是基于模板+数据的形式来进行生成,类似于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);
}

gitee项目笔记:https://gitee.com/boring8/ruo-yi-annotation.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值