开源项目的代码生成器如何开发

wen 开源项目 2

本文目录导读:

开源项目的代码生成器如何开发

  1. 第一步:明确需求与定位
  2. 第二步:核心技术架构设计
  3. 第三步:具体实现步骤(以 Java + FreeMarker 为例)
  4. 第四步:开源项目注意事项
  5. 第五步:总结与建议

开发一个开源项目的代码生成器,通常需要遵循“模板驱动 + 元数据配置 + 可扩展架构”的核心思路,目标是让用户通过简单的配置,就能自动生成重复性的CRUD代码、配置文件或整个项目骨架。

以下是一个从架构设计到具体实现的完整开发指南:

第一步:明确需求与定位

你要确定这个代码生成器的核心能力,常见的定位有:

  1. 单表CRUD生成器:输入表结构,生成 Controller、Service、Mapper、实体类、前端列表/表单页面(最主流,如 MyBatis Generator)。
  2. 项目脚手架生成器:从零生成一个完整项目(如 Spring Boot + Vue 的前后端分离项目,类似 create-vite)。
  3. 领域驱动设计(DDD)生成器:根据领域模型生成聚合、仓库、应用服务等。
  4. 模板炼金术:允许用户自定义任意代码模板,生成任何类型的文件。

建议:初学者建议从单表CRUD生成器起步,这是最刚需、最容易验证的场景。

第二步:核心技术架构设计

一个通用的代码生成器主要由以下四层构成:

[用户输入层] -> [元数据层] -> [模板引擎层] -> [输出层]

用户输入层

  • 数据来源
    • 数据库逆向(最常用):连接数据库,读取表结构、字段名、类型、注释、索引。
    • 配置文件:用户手写 JSON/YAML 定义实体和关系。
    • 可视化界面:拖拽表单或填写 Web UI(适合非技术人员)。
  • 核心注意点:需要解析数据类型映射(MySQL的 varchar -> Java的 String)。

元数据层

这是生成器的大脑,将用户输入转化为结构化的上下文对象,一般包含:

  • TableEntity:表名、表注释、主键名、所有列。
  • ColumnEntity:字段名、Java类型、JDBC类型、是否必填、是否主键、是否自增、注释。
  • GlobalConfig:项目包名、作者、输出路径、是否覆盖已存在文件。

模板引擎层

负责将元数据和模板文件结合,生成最终代码。不建议手写字符串拼接,一定要用模板引擎。

  • 首选方案FreeMarkerVelocity
    • 支持逻辑判断 (<#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 上,还需要关注:

  1. 清晰的说明文档
    • 包含快速开始示例(java -jar generator.jar -table user)。
    • 包含 TEMPLATE.md 解释如何编写自定义模板。
  2. 提供多种使用方式
    • Maven Plugin(最常用,如 mybatis-generator-maven-plugin)。
    • 命令行工具(使用 picocli 或 Spring Shell)。
    • Maven Archetype(适合生成脚手架)。
  3. 内置常见模板库
    • 至少内置一套 MyBatis-Plus + Spring Boot + MySQL 的模板。
    • 再内置一套 Spring Data JPA 的模板,满足不同用户。
  4. 版本兼容性
    • 主动说明支持的数据库(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代码生成一遍,在这个迭代过程中,你的生成器就会越来越完善。

上一篇开源项目的模板引擎如何选择

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!