1. RuoYi框架代码生成器深度解析
RuoYi框架的代码生成器是其核心功能之一,能够显著提升开发效率。作为一款基于Spring Boot的快速开发框架,RuoYi的代码生成器采用了Velocity模板引擎,实现了从数据库表结构到前后端代码的全自动生成。
1.1 代码生成器架构设计
代码生成器主要分为两大功能模块:
- 表结构管理模块:负责将数据库表结构导入系统并进行管理
- 代码生成模块:根据表结构自动生成前后端代码
这种设计遵循了"配置优于编码"的原则,开发者只需关注业务表的设计,其余重复性工作由框架自动完成。在实际项目中,这种设计可以节省约60%的基础代码编写时间。
1.2 核心数据表设计
RuoYi使用两张核心表来存储代码生成相关的元数据:
sql复制CREATE TABLE `gen_table` (
`table_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`table_name` varchar(200) DEFAULT '' COMMENT '表名称',
`table_comment` varchar(500) DEFAULT '' COMMENT '表描述',
`class_name` varchar(100) DEFAULT '' COMMENT '实体类名称',
`package_name` varchar(100) DEFAULT NULL COMMENT '生成包路径',
`module_name` varchar(30) DEFAULT NULL COMMENT '生成模块名',
`business_name` varchar(30) DEFAULT NULL COMMENT '生成业务名',
`function_name` varchar(50) DEFAULT NULL COMMENT '生成功能名',
`options` varchar(1000) DEFAULT NULL COMMENT '其它生成选项',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`table_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='代码生成业务表';
CREATE TABLE `gen_table_column` (
`column_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`table_id` varchar(64) DEFAULT NULL COMMENT '归属表编号',
`column_name` varchar(200) DEFAULT NULL COMMENT '列名称',
`column_comment` varchar(500) DEFAULT NULL COMMENT '列描述',
`column_type` varchar(100) DEFAULT NULL COMMENT '列类型',
`java_type` varchar(500) DEFAULT NULL COMMENT 'JAVA类型',
`java_field` varchar(200) DEFAULT NULL COMMENT 'JAVA字段名',
`is_pk` char(1) DEFAULT NULL COMMENT '是否主键(1是)',
`is_increment` char(1) DEFAULT NULL COMMENT '是否自增(1是)',
`is_required` char(1) DEFAULT NULL COMMENT '是否必填(1是)',
`is_insert` char(1) DEFAULT NULL COMMENT '是否为插入字段(1是)',
`is_edit` char(1) DEFAULT NULL COMMENT '是否编辑字段(1是)',
`is_list` char(1) DEFAULT NULL COMMENT '是否列表字段(1是)',
`is_query` char(1) DEFAULT NULL COMMENT '是否查询字段(1是)',
`query_type` varchar(200) DEFAULT 'EQ' COMMENT '查询方式(等于、不等于、大于、小于、范围)',
`html_type` varchar(200) DEFAULT NULL COMMENT '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)',
`dict_type` varchar(200) DEFAULT '' COMMENT '字典类型',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`column_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='代码生成业务表字段';
这两张表采用一对多的关系设计,gen_table_column表中的table_id字段关联到gen_table表的主键。这种设计既保持了数据的完整性,又便于查询和管理。
实际开发中,建议在
gen_table表上添加唯一索引uk_table_name,防止重复导入同一张表:sql复制ALTER TABLE gen_table ADD UNIQUE INDEX uk_table_name (table_name);
1.3 代码生成流程详解
1.3.1 数据库表查询与导入
当用户点击"导入"按钮时,系统执行以下流程:
- 前端调用
GenController.dataList()方法 - 后端查询数据库元数据表信息
- 返回表列表供用户选择
关键代码实现:
java复制@GetMapping("/db/list")
public TableDataInfo dataList(GenTable genTable) {
startPage();
List<GenTable> list = genTableService.selectDbTableList(genTable);
return getDataTable(list);
}
这里的selectDbTableList()方法实际上查询的是数据库的元数据表(如MySQL的information_schema.TABLES),而非业务表本身。
1.3.2 表结构导入与保存
用户选择表后,系统将表结构信息保存到gen_table和gen_table_column中:
java复制@PostMapping("/importTable")
public AjaxResult importTableSave(String tables) {
String[] tableNames = Convert.toStrArray(tables);
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
genTableService.importGenTable(tableList, SecurityUtils.getUsername());
return success();
}
实际开发中,这里需要注意事务处理,确保表头和字段信息要么全部保存成功,要么全部回滚。
1.3.3 代码生成与下载
代码生成是核心功能,其流程如下:
- 根据表名查询表结构和字段信息
- 使用Velocity模板引擎渲染各类代码文件
- 将生成的文件打包为ZIP供下载
java复制@GetMapping("/batchGenCode")
public void batchGenCode(HttpServletResponse response, String tables) throws IOException {
String[] tableNames = Convert.toStrArray(tables);
byte[] data = genTableService.downloadCode(tableNames);
genCode(response, data);
}
1.4 代码生成器配置优化
RuoYi的代码生成器提供了灵活的配置选项,位于application.yml中:
yaml复制# 代码生成
gen:
# 作者
author: ruoyi
# 默认生成包路径
packageName: com.ruoyi.project
# 自动去除表前缀,默认是false
autoRemovePre: false
# 表前缀(生成类名不会包含表前缀)
tablePrefix: sys_,tb_
实际项目中,我们通常需要修改以下配置:
- 作者信息:改为自己或团队的名字
- 包路径:改为项目实际的包结构
- 表前缀:根据项目规范设置(如
t_、biz_等)
经验分享:设置
autoRemovePre: true可以自动去除类名中的表前缀,使生成的代码更加简洁。例如表名sys_user会生成User类而非SysUser类。
2. Velocity模板引擎深度应用
2.1 Velocity核心原理
Velocity是基于Java的模板引擎,采用MVC模式实现界面与Java代码的分离。其核心工作原理如下:
- 模板文件:包含静态内容和Velocity语法(
.vm后缀) - 上下文对象:Java端提供的包含变量的数据模型
- 引擎处理:将模板与上下文合并生成最终输出
java复制// 初始化模板引擎
Velocity.init();
// 准备数据模型
VelocityContext context = new VelocityContext();
context.put("message", "Hello Velocity");
// 渲染模板
Template t = Velocity.getTemplate("hello.vm");
t.merge(context, writer);
2.2 Velocity基础语法详解
2.2.1 变量定义与使用
velocity复制#set($name = "RuoYi") ## 变量定义
${name} ## 变量输出
$!{name} ## 安静引用(变量不存在时不输出)
2.2.2 条件判断
velocity复制#if($score >= 90)
优秀
#elseif($score >= 60)
及格
#else
不及格
#end
2.2.3 循环遍历
velocity复制#foreach($item in $list)
当前项:$item
索引:$foreach.index
计数:$foreach.count
#end
2.2.4 宏定义(函数)
velocity复制#macro(showTitle $title)
<h1>$title</h1>
#end
#showTitle("RuoYi框架")
2.3 RuoYi中的模板应用
RuoYi的代码生成器使用了多个Velocity模板,主要存放在resources/vm目录下:
domain.java.vm:实体类模板mapper.java.vm:Mapper接口模板service.java.vm:Service接口模板controller.java.vm:Controller类模板mapper.xml.vm:MyBatis映射文件模板
以实体类模板为例,其核心结构如下:
velocity复制package ${packageName}.domain;
#foreach ($import in $importList)
import ${import};
#end
@Data
public class ${ClassName} extends BaseEntity {
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
@Excel(name = "${column.columnComment}")
private $column.javaType $column.javaField;
#end
}
2.4 模板自定义实战
2.4.1 增加Lombok支持
- 在
domain.java.vm模板中添加Lombok注解:
velocity复制@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ${ClassName} extends BaseEntity {
// 原有内容不变
}
- 确保项目已添加Lombok依赖:
xml复制<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.4.2 增加Swagger支持
- 修改
controller.java.vm模板:
velocity复制@Api(tags = "${functionName}管理")
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller extends BaseController {
@ApiOperation("查询${functionName}列表")
@GetMapping("/list")
public TableDataInfo list(${ClassName} ${className}) {
// 方法实现
}
}
- 添加Swagger依赖:
xml复制<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
实际项目中使用时,建议在Swagger配置类中添加全局参数配置,如Token认证等。
3. Spring Security权限控制解析
3.1 权限控制核心设计
RuoYi采用Spring Security + JWT实现权限控制,其核心架构如下:
- 认证流程:用户名密码认证 → 生成JWT Token
- 授权流程:解析Token → 获取权限信息 → 权限校验
- 安全配置:基于URL和方法的安全规则
3.2 安全配置详解
SecurityConfig是安全核心配置类,主要配置内容:
java复制@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // 禁用CSRF
.sessionManagement().sessionCreationPolicy(STATELESS) // 无状态
.and()
.authorizeRequests()
.antMatchers("/login").permitAll() // 登录接口开放
.antMatchers("/profile/**").authenticated() // 需要认证
.anyRequest().access("@ss.hasPermi(request)") // 自定义权限校验
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
}
3.3 自定义权限校验
RuoYi实现了灵活的自定义权限校验服务PermissionService:
java复制public boolean hasPermi(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
LoginUser user = getLoginUser();
if (user == null || CollectionUtils.isEmpty(user.getPermissions())) {
return false;
}
return hasPermissions(user.getPermissions(), permission);
}
使用方式:
java复制@PreAuthorize("@ss.hasPermi('system:user:list')")
public TableDataInfo list(SysUser user) {
// 方法实现
}
3.4 权限数据模型
RuoYi采用标准的RBAC权限模型:
- 用户:系统使用者
- 角色:权限集合
- 菜单:权限载体
- 部门:数据权限控制
核心表关系:
code复制用户 → 用户角色关联 → 角色 → 角色菜单关联 → 菜单
3.5 权限控制最佳实践
- 接口权限控制:
java复制@PreAuthorize("@ss.hasPermi('system:user:add')")
public AjaxResult add(@RequestBody SysUser user) {
// 业务逻辑
}
- 数据权限控制:
java复制@DataScope(deptAlias = "d", userAlias = "u")
public List<SysUser> selectUserList(SysUser user) {
return mapper.selectUserList(user);
}
- 权限缓存优化:
java复制@Cacheable(key = "'user:' + #userId")
public Set<String> getMenuPermission(Long userId) {
// 查询权限
}
实际项目中,建议将权限校验结果缓存,避免频繁查询数据库。RuoYi默认使用Redis缓存权限数据。
4. 常见问题与解决方案
4.1 代码生成器常见问题
问题1:生成的代码包路径不正确
解决方案:
- 检查
application.yml中的gen.packageName配置 - 确保表配置中的包路径正确
- 清理模板缓存后重新生成
问题2:生成的字段类型不匹配
解决方案:
- 检查
gen_table_column表中的java_type字段 - 修改
type-converter.properties中的类型映射 - 手动修改表字段的Java类型
问题3:主子表关系生成错误
解决方案:
- 确保主表和子表已正确关联
- 检查子表配置中的
subTableName和subTableFkName - 验证模板中的子表循环逻辑
4.2 Velocity模板问题
问题1:模板修改后不生效
解决方案:
- 检查模板文件是否在正确路径(
resources/vm) - 清理Velocity缓存:
VelocityEngine.reset() - 重启应用使模板更改生效
问题2:特殊字符转义问题
解决方案:
- 使用
#[[ 不解析内容 ]]#包裹特殊内容 - 对于HTML内容,使用
$esc.html()方法 - 对于JavaScript,使用
$esc.javascript()
问题3:集合操作性能差
解决方案:
- 避免在模板中进行复杂计算
- 将数据处理逻辑移到Java端
- 使用
#break指令提前终止大集合循环
4.3 权限控制问题
问题1:权限校验不生效
解决方案:
- 检查方法上的
@PreAuthorize注解 - 验证用户是否拥有对应权限编码
- 检查权限服务
PermissionService是否正常
问题2:数据权限失效
解决方案:
- 确保方法添加了
@DataScope注解 - 检查部门数据权限配置
- 验证SQL过滤器是否正确拼接条件
问题3:Token过期时间设置
解决方案:
- 修改
application.yml中的token.expireTime - 考虑实现Token自动续期
- 对于敏感操作可设置独立短过期时间
5. 高级优化与扩展
5.1 代码生成器增强
- 模板动态加载:实现数据库存储模板,支持热更新
- 多风格模板:支持不同代码风格模板切换
- 生成历史版本:记录每次生成差异,支持回滚
5.2 权限控制扩展
- 多因素认证:集成短信/邮件验证码
- 权限变更通知:权限变更时主动通知相关用户
- 权限分析报表:统计接口权限使用情况
5.3 性能优化建议
- 模板预编译:启动时预编译常用模板
- 权限缓存:使用二级缓存权限数据
- 批量操作优化:减少权限校验次数
在实际项目中使用RuoYi框架时,建议根据团队规范进行适当的定制化改造。例如,我们团队在原有代码生成器基础上增加了以下功能:
- 自动生成单元测试模板
- 支持生成TypeScript前端代码
- 集成SonarQube代码质量检查
- 自动生成API文档
这些扩展显著提升了团队的开发效率,同时保证了代码质量的一致性。