1. 引言
本教程将深入探讨 OpenAPI Generator 的自定义选项,重点介绍如何创建新生成器,为基于 Apache Camel 的应用生成 REST Producer 路由。
2. 为什么需要新生成器?
在之前的教程中,我们展示了如何自定义现有生成器的模板。但有时会遇到无法使用任何现有生成器的情况,例如需要支持新语言或 REST 框架时。
以 Apache Camel 集成框架为例:当前 OpenAPI Generator 版本仅支持生成 Consumer 路由(接收 REST 请求并转发到中介逻辑)。而当我们需要从路由调用 REST API 时,通常使用 Camel 的 REST 组件:
from(GET_QUOTE)
.id(GET_QUOTE_ROUTE_ID)
.to("rest:get:/quotes/{symbol}?outType=com.baeldung.tutorials.openapi.quotes.api.model.QuoteResponse");
这段代码有几个可自动生成的关键点:
- 从 API 定义推导接口参数
- 指定输入/输出类型
- 响应负载验证
- 跨项目统一的路由和 ID 命名
更重要的是,使用代码生成能确保当 API 演进时,生成代码始终与契约保持同步。
3. 创建 OpenAPI 生成器项目
在 OpenAPI 中,自定义生成器本质是实现了 CodegenConfig
接口的 Java 类。首先添加依赖:
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator</artifactId>
<version>7.5.0</version>
<scope>provided</scope>
</dependency>
最新版本可在 Maven Central 获取
生成器核心通过 JRE 的 Service 机制注册实现类。需在 META-INF/services
下创建文件,文件名为 CodegenConfig
实现类的全限定名(标准 Maven 项目中位于 src/main/resources
)。
也可用命令快速生成项目骨架:
mkdir -p target
wget -O target/openapi-generator-cli.jar \
https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.5.0/openapi-generator-cli-7.5.0.jar
java -jar target/openapi-generator-cli.jar meta \
-o . -n java-camel-client -p com.baeldung.openapi.generators.camelclient
4. 实现生成器
生成器需实现 CodegenConfig
接口,但该接口有 155 个方法!幸好核心提供了 DefaultCodegen
基类,只需覆盖少数方法即可实现工作生成器:
public class JavaCamelClientGenerator extends DefaultCodegen {
// 按需覆盖方法
}
4.1. 生成器元数据
首先实现 getName()
和 getTag()
:
getName()
返回用户指定的生成器名称,常用三部分标识:目标语言 + REST 框架 + 类型(client/server)getTag()
返回CodegenType
枚举值,本例为CLIENT
public String getName() {
return "java-camel-client";
}
public CodegenType getTag() {
return CodegenType.CLIENT;
}
4.2. 帮助信息
良好的用户体验需提供清晰的生成器说明,通过 getHelp()
实现:
public String getHelp() {
return "生成 Camel Producer 路由用于调用 API 操作";
}
4.3. 目标文件夹
生成器会输出多种产物:
- API 实现(客户端/服务端)
- API 测试
- API 文档
- 模型类
- 模型测试
- 模型文档
每种产物类型都有对应方法返回其生成路径,例如:
@Override
public String modelFileFolder() {
return outputFolder() + File.separator + sourceFolder +
File.separator + modelPackage().replace('.', File.separatorChar);
}
@Override
public String apiFileFolder() {
return outputFolder() + File.separator + sourceFolder +
File.separator + apiPackage().replace('.', File.separatorChar);
}
路径值来自命令行或构建工具(Maven/Gradle)传入的配置选项
4.4. 模板位置
每个生成器使用一组模板生成目标文件。内置生成器只能替换模板,而自定义生成器可自由添加新模板。
在构造函数中通过 xxxTemplateFiles()
方法注册模板,例如:
public JavaCamelClientGenerator() {
super();
// ... 其他配置省略
apiTemplateFiles().put("camel-producer.mustache", ".java");
// ... 其他配置省略
}
此代码注册 camel-producer.mustache
模板,生成的文件名 = API 名称 + 扩展名(如 .java
)。
还需通过 setTemplateDir()
设置模板基础目录:
setTemplateDir("java-camel-client");
4.5. 配置选项
大多数生成器需要用户配置参数,在构造函数中通过 cliOptions()
注册:
public JavaCamelClientGenerator() {
// ... 其他配置省略
cliOptions().add(
new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC)
.defaultValue(apiPackage));
cliOptions().add(
new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC)
.defaultValue(sourceFolder));
}
✅ 优先使用
CodegenConstants
中的标准选项名,提升用户迁移体验
4.6. 处理配置选项
生成器核心在开始生成前调用 processOpts()
,这是设置生成前状态的最后机会:
public void processOpts() {
super.processOpts();
if (additionalProperties().containsKey(CodegenConstants.SOURCE_FOLDER)) {
sourceFolder = ((String) additionalProperties().get(CodegenConstants.SOURCE_FOLDER));
// ... 源文件夹验证省略
}
}
⚠️ 当前验证失败只能抛 RuntimeException
(如 IllegalArgumentException
),会附带冗长堆栈信息,体验不佳。
4.7. 附加文件
可生成与 API/模型无关的文件(如 pom.xml
、README
等),通过构造函数中 supportingFiles()
添加:
public JavaCamelClientGenerator() {
// ... 其他配置省略
supportingFiles().add(new SupportingFile("readme.mustache", "", "README.txt"));
}
SupportingFile
是三元组:模板名、相对输出目录、输出文件名。
4.8. 模板辅助类
默认模板引擎 Mustache 数据处理能力有限(如无字符串操作)。需通过实现 Mustache.Lambda
的辅助类扩展:
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
ImmutableMap.Builder<String, Mustache.Lambda> builder = super.addMustacheLambdas();
return builder
.put("javaconstant", new JavaConstantLambda())
.put("path", new PathLambda());
}
在模板中通过 lambda
上下文调用:
{{#lambda.javaconstant}}... 任意 Mustache 内容 ...{{/lambda.javaconstant}}
以 PathLambda
为例(从 URL 提取路径):
public class PathLambda implements Mustache.Lambda {
@Override
public void execute(Template.Fragment fragment, Writer writer) throws IOException {
String maybeUri = fragment.execute();
try {
URI uri = new URI(maybeUri);
if (uri.getPath() != null) {
writer.write(uri.getPath());
} else {
writer.write("/");
}
}
catch (URISyntaxException e) {
writer.write(maybeUri); // 非 URI 则原样输出
}
}
}
4.9. 模板编写
这是最耗时的部分,但可参考现有模板。本例假设生成 Spring Boot 应用组件,每个 API 生成一个继承 RouteBuilder
的 @Component
类。
每个操作添加一个 "direct" 路由,使用 DSL 定义 REST 目标。完整模板见GitHub,生产环境需补充错误处理、重试策略等。
5. 单元测试
使用 CodegenConfigurator
进行基础功能测试:
public void whenLaunchCodeGenerator_thenSuccess() throws Exception {
Map<String, Object> opts = new HashMap<>();
opts.put(CodegenConstants.SOURCE_FOLDER, "src/generated");
opts.put(CodegenConstants.API_PACKAGE, "test.api");
CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java-camel-client")
.setInputSpec("petstore.yaml")
.setAdditionalProperties(opts)
.setOutputDir("target/out/java-camel-client");
ClientOptInput clientOptInput = configurator.toClientOptInput();
DefaultGenerator generator = new DefaultGenerator();
generator.opts(clientOptInput)
.generate();
File f = new File("target/out/java-camel-client/src/generated/test/api/PetApi.java");
assertTrue(f.exists());
}
此测试验证生成文件是否出现在预期位置(本例为按 API 标签命名的 Java 文件)。
6. 集成测试
单元测试无法验证生成代码的运行时行为。更可靠的方法是创建专用测试项目,在示例项目中添加 OpenAPI Generator 插件:
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator.version}</version>
<configuration>
<skipValidateSpec>true</skipValidateSpec>
<inputSpec>${project.basedir}/src/main/resources/api/quotes.yaml</inputSpec>
</configuration>
<executions>
<execution>
<id>generate-camel-client</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<generatorName>java-camel-client</generatorName>
<generateModels>false</generateModels>
<configOptions>
<apiPackage>com.baeldung.tutorials.openapi.quotes.client</apiPackage>
<modelPackage>com.baeldung.tutorials.openapi.quotes.api.model</modelPackage>
</configOptions>
</configuration>
</execution>
<!-- 其他执行省略 -->
</executions>
<dependencies>
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>openapi-custom-generator</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
<!-- 其他插件省略 -->
</plugins>
关键点:将自定义生成器作为插件依赖,才能在
generatorName
中指定java-camel-client
由于本生成器不支持模型生成,完整 pom.xml
中添加了第二个执行块使用标准 Java 生成器。使用 Camel 测试支持类验证生成代码:
@SpringBootTest
class ApplicationUnitTest {
@Autowired
private FluentProducerTemplate producer;
@Autowired
private CamelContext camel;
@Test
void whenInvokeGeneratedRoute_thenSuccess() throws Exception {
AdviceWith.adviceWith(camel, QuotesApi.GET_QUOTE_ROUTE_ID, in -> {
in.mockEndpointsAndSkip("rest:*");
});
Exchange exg = producer.to(QuotesApi.GET_QUOTE)
.withHeader("symbol", "BAEL")
.send();
assertNotNull(exg);
}
}
7. 总结
本教程展示了创建 OpenAPI 自定义生成器的完整流程,并通过测试项目验证了生成代码的实用性。所有代码可在 GitHub 获取。