本文目录导读:

开发一个开源项目的代码生成器,通常需要遵循“模板驱动 + 元数据配置 + 可扩展架构”的核心思路,目标是让用户通过简单的配置,就能自动生成重复性的CRUD代码、配置文件或整个项目骨架。
以下是一个从架构设计到具体实现的完整开发指南:
第一步:明确需求与定位
你要确定这个代码生成器的核心能力,常见的定位有:
- 单表CRUD生成器:输入表结构,生成 Controller、Service、Mapper、实体类、前端列表/表单页面(最主流,如 MyBatis Generator)。
- 项目脚手架生成器:从零生成一个完整项目(如 Spring Boot + Vue 的前后端分离项目,类似
create-vite)。 - 领域驱动设计(DDD)生成器:根据领域模型生成聚合、仓库、应用服务等。
- 模板炼金术:允许用户自定义任意代码模板,生成任何类型的文件。
建议:初学者建议从单表CRUD生成器起步,这是最刚需、最容易验证的场景。
第二步:核心技术架构设计
一个通用的代码生成器主要由以下四层构成:
[用户输入层] -> [元数据层] -> [模板引擎层] -> [输出层]
用户输入层
- 数据来源:
- 数据库逆向(最常用):连接数据库,读取表结构、字段名、类型、注释、索引。
- 配置文件:用户手写 JSON/YAML 定义实体和关系。
- 可视化界面:拖拽表单或填写 Web UI(适合非技术人员)。
- 核心注意点:需要解析数据类型映射(MySQL的
varchar-> Java的String)。
元数据层
这是生成器的大脑,将用户输入转化为结构化的上下文对象,一般包含:
- TableEntity:表名、表注释、主键名、所有列。
- ColumnEntity:字段名、Java类型、JDBC类型、是否必填、是否主键、是否自增、注释。
- GlobalConfig:项目包名、作者、输出路径、是否覆盖已存在文件。
模板引擎层
负责将元数据和模板文件结合,生成最终代码。不建议手写字符串拼接,一定要用模板引擎。
- 首选方案:FreeMarker 或 Velocity。
- 支持逻辑判断 (
<#if>)、循环 (<#list>)、变量引用 (${entity.name})。 - 文件结构清晰,易于调试。
- 支持逻辑判断 (
- 备选方案(Java生态外):
- Node.js:EJS、Handlebars(适合前后端混合项目生成器)。
- Python:Jinja2。
输出层
- 输出路径:通常用户指定根目录,生成器自动创建包路径对应的文件夹。
- 文件组织:
output/ ├── src/main/java/com/example/ │ ├── controller/ # UserController.java │ ├── service/ # UserService.java │ └── model/ # User.java, UserVO.java └── src/main/resources/ └── mapper/ # UserMapper.xml
第三步:具体实现步骤(以 Java + FreeMarker 为例)
假设我们要生成一个针对 sys_user 表的 Controller 和 Entity。
搭建项目骨架
创建一个 Maven 项目,引入核心依赖:
<!-- 模板引擎 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
<!-- 数据库连接(用于逆向) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
编写模板文件(.ftl)
在 src/main/resources/templates/ 下创建:
Entity.java.ftl(核心模板示例)
package ${packageName}.model;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.util.Date;
public class ${entityName} implements Serializable {
private static final long serialVersionUID = 1L;
<#list columns as column>
/** ${column.comment} */
<#if column.isPrimaryKey>
@TableId
</#if>
private ${column.javaType} ${column.fieldName};
</#list>
<#list columns as column>
public ${column.javaType} get${column.fieldName?cap_first}() {
return ${column.fieldName};
}
public void set${column.fieldName?cap_first}(${column.javaType} ${column.fieldName}) {
this.${column.fieldName} = ${column.fieldName};
}
</#list>
}
注意:?cap_first 是 FreeMarker 内置函数,用于将首字母大写。
编写核心生成逻辑(Java 类)
public class CodeGenerator {
private Configuration cfg;
private String packageName = "com.example.demo";
private String outputDir = "./output/";
// 初始化 FreeMarker
public void init() throws Exception {
cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setClassForTemplateLoading(this.getClass(), "/templates");
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
}
// 核心生成方法
public void generate(String tableName) throws Exception {
// 1. 查询数据库,获取元数据(使用 JDBC 的 DatabaseMetaData)
TableMetaData tableMeta = getTableMeta(tableName); // 自定义方法
// 2. 构建模板数据模型
Map<String, Object> root = new HashMap<>();
root.put("packageName", packageName);
root.put("entityName", toCamelCase(tableName)); // sys_user -> SysUser
root.put("columns", tableMeta.getColumns());
// 3. 渲染模板
Template template = cfg.getTemplate("Entity.java.ftl");
File outputFile = new File(outputDir + "/model/" + root.get("entityName") + ".java");
outputFile.getParentFile().mkdirs();
try (Writer out = new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)) {
template.process(root, out);
}
System.out.println("生成成功: " + outputFile.getAbsolutePath());
}
}
增加高级功能(差异化竞争力)
- 多模板支持:不仅生成 Entity,还生成 Controller、Service、Mapper、DTO、VO。
- 策略模式:允许用户选择 “只生成 Entity” 或 “生成全部”。
- 自定义类型映射:
private String mapType(String dbType) { switch (dbType.toUpperCase()) { case "VARCHAR": case "CHAR": return "String"; case "INT": case "BIGINT": return dbType.toLowerCase(); case "DATETIME": case "TIMESTAMP": return "Date"; default: return "String"; } } - 支持 Swagger 注解:在模板中判断字段注释,自动生成
@ApiModelProperty。 - 插件系统:允许用户通过 SPI 或类加载机制,插入自定义的“预处理”或“后处理”插件。
第四步:开源项目注意事项
如果你打算把这个项目开源放在 GitHub/Gitee 上,还需要关注:
- 清晰的说明文档:
- 包含快速开始示例(
java -jar generator.jar -table user)。 - 包含
TEMPLATE.md解释如何编写自定义模板。
- 包含快速开始示例(
- 提供多种使用方式:
- Maven Plugin(最常用,如 mybatis-generator-maven-plugin)。
- 命令行工具(使用 picocli 或 Spring Shell)。
- Maven Archetype(适合生成脚手架)。
- 内置常见模板库:
- 至少内置一套 MyBatis-Plus + Spring Boot + MySQL 的模板。
- 再内置一套 Spring Data JPA 的模板,满足不同用户。
- 版本兼容性:
- 主动说明支持的数据库(MySQL、PostgreSQL、Oracle、SQL Server)。
- 支持 Java 8+ 和 Spring Boot 2.x/3.x。
第五步:总结与建议
- 不要重复造轮子:深入研究 MyBatis Generator、MyBatis-Plus Generator,看看它们的源码,理解它们是怎么处理表关联、反向映射的。
- 关注用户体验:一个好的代码生成器不只是生成代码,更要生成符合团队规范的、可读性强的代码,建议在模板中预留
@Builder或@Data等 Lombok 注解,或者在配置中让用户选择。 - 做减法:通用生成器容易变得臃肿,可以考虑只做核心生成引擎,把模板仓库交给社区(类似 VS Code 插件市场)。
如果你不想从零开始,可以考虑基于以下项目二次开发:
- MyBatis-Plus Generator(Java,生态最好)
- lowcode-engine(阿里出品的低代码引擎,适合可视化生成器)
- Plop(Node.js 生态,适合生成前端组件/页面)
开始动手的最佳路径是:找到一个你日常用得最多的项目,用代码生成器先把它的全部CRUD代码生成一遍,在这个迭代过程中,你的生成器就会越来越完善。