代码生成器实践
代码生成器通过模板批量生成重复代码,提升开发效率,保证代码一致性。
1. 常见方案对比
| 方案 | 特点 | 适用场景 |
|---|---|---|
| MyBatis-Plus Generator | 官方出品、生成 Mapper/Entity/Service | 单表 CRUD |
| SpringBlade | 生成完整前后端代码 | 微服务 |
| Ruoyi | 若依框架配套、Vue+Java | 管理系统 |
| OpenAPI Generator | 根据 API 文档生成客户端 | API 消费方 |
| Lombok+MapStruct | 减少模板代码 | DTO/VO 转换 |
| 自定义生成器 | 按项目需求定制 | 特殊项目 |
2. MyBatis-Plus Generator
2.1 Maven 依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
2.2 生成配置
public class GeneratorApp {
public static void main(String[] args) {
FastAutoGenerator.create(
"jdbc:mysql://localhost:3306/test?useSSL=false",
"root",
"password"
)
.globalConfig(builder -> {
builder.author("generator") // 作者
.outputDir("src/main/java") // 输出路径
.disableOpenDir(); // 生成后不打开目录
})
.packageConfig(builder -> {
builder.parent("com.example") // 父包名
.moduleName("gen") // 模块名
.entity("entity") // 实体类包名
.service("service")
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper.xml");
})
.strategyConfig(builder -> {
builder.addInclude("sys_user") // 表名
.addTablePrefix("sys_") // 过滤表前缀
// Entity 配置
.entityBuilder()
.enableLombok()
.enableTableFieldAnnotation()
.logicDeleteColumnName("deleted")
// Controller 配置
.controllerBuilder()
.enableRestStyle()
.enableHyphenStyle()
// Service 配置
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl");
})
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
3. 自定义代码生成器
3.1 项目结构
generator/
├── src/main/java/
│ └── com/example/generator/
│ ├── GeneratorApplication.java
│ ├── GeneratorController.java
│ ├── GeneratorService.java
│ └── util/
│ └── TableMetaUtil.java
├── src/main/resources/
│ └── templates/ # FreeMarker 模板
│ ├── entity.ftl
│ ├── mapper.ftl
│ ├── service.ftl
│ ├── serviceImpl.ftl
│ └── controller.ftl
└── generator.yml # 生成配置
3.2 表元数据获取
@Service
public class TableMetaUtil {
@Autowired
private DataSource dataSource;
public TableMeta getTableMeta(String tableName) {
TableMeta meta = new TableMeta();
meta.setTableName(tableName);
// 获取列信息
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData dbMeta = conn.getMetaData();
try (ResultSet columns = dbMeta.getColumns(null, null, tableName, null)) {
List<ColumnMeta> columns = new ArrayList<>();
while (columns.next()) {
ColumnMeta col = new ColumnMeta();
col.setColumnName(columns.getString("COLUMN_NAME"));
col.setColumnType(columns.getString("TYPE_NAME"));
col.setComment(columns.getString("REMARKS"));
col.setNullable("YES".equals(columns.getString("IS_NULLABLE")));
columns.add(col);
}
meta.setColumns(columns);
}
}
return meta;
}
}
3.3 生成服务
@Service
public class GeneratorService {
public void generate(GeneratorRequest request) {
// 1. 获取表元数据
TableMeta tableMeta = tableMetaUtil.getTableMeta(request.getTableName());
// 2. 准备数据
Map<String, Object> data = prepareData(tableMeta, request);
// 3. 生成各层代码
generateFile("templates/entity.ftl", data,
request.getBasePackage(), "entity", tableMeta.getEntityName() + ".java");
generateFile("templates/mapper.ftl", data,
request.getBasePackage(), "mapper", tableMeta.getEntityName() + "Mapper.java");
generateFile("templates/service.ftl", data,
request.getBasePackage(), "service", tableMeta.getEntityName() + "Service.java");
generateFile("templates/serviceImpl.ftl", data,
request.getBasePackage(), "service/impl",
tableMeta.getEntityName() + "ServiceImpl.java");
generateFile("templates/controller.ftl", data,
request.getBasePackage(), "controller",
tableMeta.getEntityName() + "Controller.java");
}
private void generateFile(String template, Map<String, Object> data,
String basePackage, String subPackage, String fileName) {
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setClassLoaderForTemplateLoading(
getClass().getClassLoader(), "/templates");
Template tp = cfg.getTemplate(template);
Path outputPath = Paths.get(
request.getOutputDir(),
basePackage.replace('.', '/'),
subPackage,
fileName
);
Files.createDirectories(outputPath.getParent());
try (Writer writer = new FileWriter(outputPath.toFile())) {
tp.process(data, writer);
}
} catch (Exception e) {
throw new RuntimeException("生成文件失败", e);
}
}
}
3.4 Entity 模板示例
package ${basePackage}.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* ${tableMeta.tableComment}
* @author ${author}
*/
@Data
@TableName("${tableMeta.tableName}")
public class ${tableMeta.entityName} implements Serializable {
<#list tableMeta.columns as column>
<#if column.columnName == "id">
@TableId(type = IdType.ASSIGN_ID)
</#if>
/**
* ${column.columnComment}
*/
<#if column.columnType == "DATETIME" || column.columnType == "TIMESTAMP">
private LocalDateTime ${column.fieldName};
<#elseif column.columnType == "BIGINT">
private Long ${column.fieldName};
<#elseif column.columnType == "INT">
private Integer ${column.fieldName};
<#elseif column.columnType == "VARCHAR">
private String ${column.fieldName};
<#else>
private Object ${column.fieldName};
</#if>
</#list>
}
4. OpenAPI 生成客户端
# openapi.yaml
openapi: 3.0.0
info:
title: 用户API
paths:
/users/{id}:
get:
operationId: getUser
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/User'
# 使用 openapi-generator 生成 Java 客户端
openapi-generator generate \
-i openapi.yaml \
-g java \
-o ./generated-client \
--api-package=com.example.api \
--model-package=com.example.model
# 生成 TypeScript
openapi-generator generate \
-i openapi.yaml \
-g typescript-fetch \
-o ./generated-client
5. 注意事项
- 生成后必检:生成的代码必须 review,不能直接用于生产
- 模板版本管理:模板也是代码,进 Git 管理
- 命名规范:生成时注意类名、表名符合项目命名规范
- 防止覆盖:已修改的文件不要被重新生成覆盖
- 字段映射:数据库类型到 Java 类型要完整映射(时间类型、枚举等)
6. 推荐工作流
1. 设计数据库表(ER图)
↓
2. 运行代码生成器
↓
3. 生成基础 CRUD 代码
↓
4. 补充业务逻辑(生成代码只做骨架)
↓
5. 添加 Service 层复杂逻辑
↓
6. 编写单元测试
最后更新:2026/05/11