10分钟上手RuoYi代码生成器:Velocity模板引擎实战指南
你还在手写CRUD代码吗?还在为重复的表单页面开发烦恼吗?RuoYi框架的代码生成器(Code Generator)让这一切成为历史!本文将带你深入了解基于Velocity模板引擎的代码生成原理,通过3个实战案例掌握自定义模板技巧,彻底解放双手,让你专注于业务逻辑而非重复劳动。
读完本文你将获得:
- 掌握RuoYi代码生成器的核心工作流程
- 学会自定义Velocity模板实现个性化代码生成
- 理解表结构与代码模板的映射关系
- 解决90%的模板开发常见问题
代码生成器核心架构
RuoYi代码生成器基于Velocity模板引擎实现,通过数据库表结构解析→模板变量映射→代码文件渲染的三步流程,自动生成完整的前后端代码。核心实现位于ruoyi-generator模块,主要包含模板管理、变量处理和代码生成三大功能。
核心实现类
| 类名 | 职责 | 核心方法 |
|---|---|---|
| VelocityUtils.java | 模板上下文构建 | prepareContext()、getTemplateList() |
| GenUtils.java | 表结构解析 | initTable()、initColumnField() |
| GenController.java | 生成入口控制 | generateCode()、previewCode() |
生成流程解析
代码生成的核心流程在VelocityUtils.prepareContext()方法中实现,该方法将数据库表结构信息转换为Velocity模板引擎可识别的上下文变量:
public static VelocityContext prepareContext(GenTable genTable) {
VelocityContext velocityContext = new VelocityContext();
velocityContext.put("tableName", genTable.getTableName());
velocityContext.put("ClassName", genTable.getClassName());
velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
velocityContext.put("columns", genTable.getColumns());
// 更多变量映射...
return velocityContext;
}
这些变量将在模板文件中被引用,最终渲染出完整的Java代码、XML配置和HTML页面。
模板文件结构
RuoYi框架内置了丰富的Velocity模板文件,位于项目的vm目录下,按照生成的代码类型分类组织:
标准模板列表
通过VelocityUtils.getTemplateList()方法可以查看系统支持的模板类型:
public static List<String> getTemplateList(String tplCategory) {
List<String> templates = new ArrayList<String>();
templates.add("vm/java/domain.java.vm"); // 实体类模板
templates.add("vm/java/mapper.java.vm"); // Mapper接口模板
templates.add("vm/java/service.java.vm"); // Service接口模板
templates.add("vm/java/serviceImpl.java.vm");// Service实现类模板
templates.add("vm/java/controller.java.vm"); // 控制器模板
templates.add("vm/xml/mapper.xml.vm"); // MyBatis映射文件模板
// HTML模板根据不同类型添加
if (GenConstants.TPL_CRUD.equals(tplCategory)) {
templates.add("vm/html/list.html.vm"); // 列表页模板
}
// ...更多模板
return templates;
}
模板文件位置
所有模板文件遵循统一的命名规范,存放在以下路径:
vm/
├── java/ # Java代码模板
│ ├── domain.java.vm # 实体类
│ ├── mapper.java.vm # Mapper接口
│ ├── service.java.vm # Service接口
│ └── ...
├── xml/ # XML配置模板
│ └── mapper.xml.vm # MyBatis映射文件
└── html/ # HTML页面模板
├── list.html.vm # 列表页
├── add.html.vm # 添加页
└── edit.html.vm # 编辑页
实战案例:自定义业务模板
案例1:扩展实体类模板
假设我们需要为所有生成的实体类添加创建时间和更新时间字段,并使用@JsonFormat注解格式化日期。
原模板代码(domain.java.vm)
package ${packageName}.domain;
#if(${hasBigDecimal})
import java.math.BigDecimal;
#end
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
/**
* ${functionName}对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
public class ${ClassName} extends BaseEntity
{
private static final long serialVersionUID = 1L;
#foreach($column in $columns)
#if($column.isPk)
/** 主键 */
@Excel(name = "${column.columnComment}", cellType = Excel.ColumnType.NUMERIC)
private ${column.javaType} ${column.javaField};
#end
#end
#foreach($column in $columns)
#if(!$column.isPk)
/** ${column.columnComment} */
#if($column.isExcel)
@Excel(name = "${column.columnComment}"
#if($column.readConverterExp)
, readConverterExp = "${column.readConverterExp}"
#end
#if($column.dictType)
, dictType = "${column.dictType}"
#end
)
#end
private ${column.javaType} ${column.javaField};
#end
#end
#foreach($column in $columns)
#getSetMethod($column)
#end
}
自定义模板修改
- 添加创建时间和更新时间字段定义:
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
- 在getter/setter方法区域添加对应方法:
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
案例2:自定义查询条件模板
RuoYi默认生成的列表查询只包含简单的等值查询,我们可以扩展mapper.xml.vm模板,添加范围查询条件:
<select id="select${ClassName}List" parameterType="${ClassName}Query" resultMap="${ClassName}Result">
select #allColumnSql() from ${tableName}
<where>
#foreach($column in $columns)
#if($column.isQuery)
<if test="${column.javaField} != null
#if($column.javaType == 'String')
and ${column.javaField} != ''
#end
">
#if($column.queryType == 'LIKE')
AND ${column.columnName} LIKE CONCAT('%', #{${column.javaField}}, '%')
#elseif($column.queryType == 'BETWEEN')
AND ${column.columnName} BETWEEN #{${column.javaField}Start} AND #{${column.javaField}End}
#else
AND ${column.columnName} = #{${column.javaField}}
#end
</if>
#end
#end
</where>
</select>
案例3:优化前端表格模板
默认的列表页表格只显示基础字段,我们可以修改list.html.vm模板,添加状态标签和操作按钮组:
<table id="bootstrap-table" class="table table-striped table-hover">
<thead>
<tr>
#foreach($column in $columns)
#if($column.isList)
<th data-field="${column.javaField}"
#if($column.columnName == 'status')
data-formatter="statusFormatter"
#end
>${column.columnComment}</th>
#end
#end
<th data-field="operate" data-formatter="operateFormatter" data-events="operateEvents">操作</th>
</tr>
</thead>
</table>
<script>
// 状态格式化函数
function statusFormatter(value, row, index) {
var color = value === '0' ? 'success' : 'danger';
var text = value === '0' ? '正常' : '禁用';
return '<span class="label label-' + color + '">' + text + '</span>';
}
// 操作按钮格式化函数
function operateFormatter(value, row, index) {
return [
'<a class="btn btn-primary btn-xs" href="javascript:edit(\'' + row.id + '\')">编辑</a>',
'<a class="btn btn-danger btn-xs" href="javascript:remove(\'' + row.id + '\')">删除</a>'
].join('');
}
</script>
高级技巧:模板变量详解
Velocity模板中可以使用的变量主要来自VelocityUtils.prepareContext()方法的设置,常用变量包括:
表级变量
| 变量名 | 含义 | 示例 |
|---|---|---|
| ${tableName} | 数据库表名 | sys_user |
| ${ClassName} | 实体类名 | SysUser |
| ${className} | 实例变量名 | sysUser |
| ${moduleName} | 模块名 | system |
| ${businessName} | 业务名 | user |
| ${packageName} | 包名 | com.ruoyi.system |
列级变量
在遍历${columns}时可使用的字段:
#foreach($column in $columns)
${column.columnName} // 数据库列名
${column.javaField} // Java字段名
${column.javaType} // Java类型
${column.columnComment} // 字段注释
${column.isPk} // 是否主键
${column.isRequired} // 是否必填
${column.queryType} // 查询类型
#end
自定义变量扩展
如果内置变量不足以满足需求,可以在VelocityUtils.java的prepareContext()方法中添加自定义变量:
// 添加自定义变量
velocityContext.put("basePath", GenConfig.getBasePath());
velocityContext.put("copyright", "Copyright © 2025 RuoYi Framework");
然后在模板中直接使用:
/**
* ${functionName}对象 ${tableName}
* ${copyright}
*/
public class ${ClassName} extends BaseEntity {
// ...
}
常见问题解决
模板不生效问题
- 检查模板路径:确保自定义模板放在正确的
vm子目录下 - 清理缓存:RuoYi会缓存模板内容,修改后需重启应用或清理
target目录 - 检查文件名:模板文件名必须以
.vm为后缀,如domain.java.vm
变量未解析问题
当模板中出现${variable}原样输出时,通常是因为:
- 变量名拼写错误,如将
${ClassName}误写为${classname} - 上下文未设置该变量,需检查
VelocityUtils.prepareContext()是否添加了对应变量 - 数据库表结构变更后未重新同步,需在代码生成页面点击"同步数据库"
代码格式错乱问题
Velocity模板对缩进敏感,建议:
- 使用空格而非Tab缩进
- 控制语句使用
#foreach和#end时保持统一缩进 - 复杂逻辑拆分到多个模板片段
总结与进阶
通过本文的学习,你已经掌握了RuoYi代码生成器的核心原理和模板自定义技巧。合理使用代码生成器可以将开发效率提升60%以上,让你从重复的CRUD工作中解放出来。
进阶学习路径
- 深入Velocity语法:学习宏定义(
#macro)和指令(#parse)实现复杂模板逻辑 - 动态模板选择:通过
GenTable的tplCategory字段实现不同业务类型使用不同模板集 - 模板版本管理:将常用模板提交到Git进行版本控制,实现团队共享
实用资源
- 官方模板参考:
ruoyi-generator/src/main/resources/vm/目录下的所有模板文件 - 数据库设计规范:RuoYi数据库设计指南
- 在线Velocity测试工具:可用于验证模板语法正确性
现在就动手改造你的第一个模板吧!无论是添加统一的日志打印,还是实现复杂的业务逻辑生成,RuoYi代码生成器都能成为你提高开发效率的得力助手。
本文示例代码已同步至项目仓库,可在
docs/code-generator-examples目录下查看完整案例。有任何问题欢迎在项目Issues中交流讨论。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



