swagger-codegen可以通过命令行生成代码(v2.1.5 /Swagger格式),也可以通过web服务生成代码(v3.0.11/OpenAPI格式)。两者只是入口不一样,但实际都是调用的DefaultGenerator.generate()来生成代码。
在整个生成代码的过程中,主要是 OpenAPI/Swagger,不同语言的CodegenConfig实现类(可以继承这个来实现自定义的处理方式),DefaultGenerator三者发挥着重要作用。
v2.1.5源码:https://github.com/swagger-api/swagger-codegen
v3.0.11源码:https://github.com/swagger-api/swagger-codegen/tree/v3.0.11
SwaggerCodegen.main:
public class SwaggerCodegen {
public static void main(String[] args) {
String version = Version.readVersionFromResources();
@SuppressWarnings("unchecked")
Cli.CliBuilder<Runnable> builder =
Cli.<Runnable>builder("swagger-codegen-cli")
.withDescription(
String.format(
"Swagger code generator CLI (version %s). More info on swagger.io",
version))
.withDefaultCommand(Langs.class)
//这里有Generate.class,其内部有run方法
.withCommands(Generate.class, Meta.class, Langs.class, Help.class,
ConfigHelp.class, Validate.class, Version.class);
//调用上述Generate的run()
builder.build().parse(args).run();
}
}
Generate.run:
public void run() {
......
//设置代码数据
if (isNotEmpty(spec)) {
configurator.setInputSpec(spec);
}
//设置语言种类
if (isNotEmpty(lang)) {
configurator.setLang(lang);
}
//设置各种参数值
......
applySystemPropertiesKvpList(systemProperties, configurator);
applyInstantiationTypesKvpList(instantiationTypes, configurator);
applyImportMappingsKvpList(importMappings, configurator);
applyTypeMappingsKvpList(typeMappings, configurator);
applyAdditionalPropertiesKvpList(additionalProperties, configurator);
applyLanguageSpecificPrimitivesCsvList(languageSpecificPrimitives, configurator);
applyReservedWordsMappingsKvpList(reservedWordsMappings, configurator);
//转换成ClientOptInput
final ClientOptInput clientOptInput = configurator.toClientOptInput();
new DefaultGenerator().opts(clientOptInput).generate();
}
CodegenConfigurator.toClientOptInput:
public ClientOptInput toClientOptInput() {
Validate.notEmpty(lang, "language must be specified");
Validate.notEmpty(inputSpec, "input spec must be specified");
setVerboseFlags();
setSystemProperties();
CodegenConfig config = CodegenConfigLoader.forName(lang);
//设置各种属性
......
//处理动态属性至additionalProperties(如:XXXCodegen)
handleDynamicProperties(config);
if (isNotEmpty(library)) {
config.setLibrary(library);
}
config.additionalProperties().putAll(additionalProperties);
//设置对应语言的CodegenConfig
ClientOptInput input = new ClientOptInput()
.config(config);
final List<AuthorizationValue> authorizationValues = AuthParser.parse(auth);
//读取输入的数据
Swagger swagger = new SwaggerParser().read(inputSpec, authorizationValues, true);
input.opts(new ClientOpts())
.swagger(swagger);
return input;
}
GeneratorController
package io.swagger.v3.generator.online;
public ResponseContext generate(RequestContext context, GenerationRequest generationRequest) {
//预处理一些信息(比如:处理生成代码的路径)
......
//调用生成代码的接口
ResponseContext responseContext = generate(generationRequest, outputRootFolder, outputContentFolder, outputFile);
LOGGER.debug("generate end - " + requestLog);
return responseContext;
}
private ResponseContext generate(GenerationRequest generationRequest, File outputRootFolder, File outputContentFolder, File outputFile) {
//生成器服务,用于配置CodegenConfig等
GeneratorService generatorService = new GeneratorService();
try {
generatorService.generationRequest(generationRequest);
} catch (Exception e) {
String msg = "Error processing generation request: " + e.getMessage();
LOGGER.error(msg, e);
return new ResponseContext()
.status(400)
.contentType(MediaType.TEXT_PLAIN)
.entity(msg);
}
final List<File> files;
try {
//1.生成代码!
files = generatorService.generate();
} catch (Exception e) {
String msg = String.format("Error generating `%s` code : %s", generationRequest.getLang(), e.getMessage());
LOGGER.error(msg, e);
return new ResponseContext()
.status(500)
.contentType(MediaType.TEXT_PLAIN)
.entity(msg);
}
......
}
1.生成代码 GeneratorService.generate:也是调用的DefaultGenerator.opts(XXX)和DefaultGenerator.generate()
opts方法主要是配置相关的信息,生成代码还是靠generate方法
public List<File> generate() {
//v3对应着3.0.11 OpenAPI的代码数据
if (optsV3 != null) {
return new DefaultGenerator().opts(optsV3).generate();
} else if (optsV2 != null) {
//v2对应着swagger的代码数据
return new io.swagger.codegen.DefaultGenerator().opts(optsV2).generate();
}
throw new RuntimeException("missing opts input");
}
这里分析的是3.0.11的DefaultGenerator
DefaultGenerator.generate:
生成各种文件的流程是:处理数据,找到模板,生成文件
public List<File> generate() {
if (swagger == null || config == null) {
throw new RuntimeException("missing swagger input or config!");
}
//添加一些生成器属性(名字,版本,生成时间等)和测试数据
configureGeneratorProperties();
//添加appname,license等信息
configureSwaggerInfo();
//处理model里面内联(包括array)
InlineModelResolver inlineModelResolver = new InlineModelResolver();
inlineModelResolver.flatten(swagger);
List<File> files = new ArrayList<File>();
// 1.生成models
List<Object> allModels = new ArrayList<Object>();
generateModels(files, allModels);
// 2.生成apis
List<Object> allOperations = new ArrayList<Object>();
generateApis(files, allOperations, allModels);
// 3.生成支持文件的数据
Map<String, Object> bundle = buildSupportFileBundle(allOperations, allModels);
// 生成支持文件,就是除去api和model所需要的文件
generateSupportingFiles(files, bundle);
config.processSwagger(swagger);
//返回生成的所有文件
return files;
}
private void generateModels(List<File> files, List<Object> allModels) {
if (!generateModels) {
return;
}
//获取所有的model数据
final Map<String, Schema> schemas = this.openAPI.getComponents().getSchemas();
if (schemas == null) {
return;
}
......
for (String name : modelKeys) {
try {
//不需要生成可以导入的类
if(config.importMapping().containsKey(name)) {
LOGGER.info("Model " + name + " not imported due to import mapping");
continue;
}
Schema schema = schemas.get(name);
Map<String, Schema> schemaMap = new HashMap<>();
schemaMap.put(name, schema);
//处理model数据(包括了包路径,属性信息)
Map<String, Object> models = processModels(config, schemaMap, schemas);
//存放类名
//toModelName多处使用,为了统一名字格式(非字母下划线数字字符会被去掉)
models.put("classname", config.toModelName(name));
models.putAll(config.additionalProperties());
allProcessedModels.put(name, models);
final List<Object> modelList = (List<Object>) models.get("models");
if (modelList == null || modelList.isEmpty()) {
continue;
}
//处理内部类?
for (Object object : modelList) {
Map<String, Object> modelMap = (Map<String, Object>) object;
CodegenModel codegenModel = null;
if (modelMap.containsKey("oneOf-model")) {
codegenModel = (CodegenModel) modelMap.get("oneOf-model");
}
if (modelMap.containsKey("anyOf-model")) {
codegenModel = (CodegenModel) modelMap.get("anyOf-model");
}
if (codegenModel != null) {
models = processModel(codegenModel, config, schemas);
models.put("classname", config.toModelName(codegenModel.name));
models.putAll(config.additionalProperties());
allProcessedModels.put(codegenModel.name, models);
break;
}
}
} catch (Exception e) {
throw new RuntimeException("Could not process model '" + name + "'" + ".Please make sure that your schema is correct!", e);
}
}
// 预处理model
allProcessedModels = config.postProcessAllModels(allProcessedModels);
for (String modelName: allProcessedModels.keySet()) {
//获取model信息
Map<String, Object> models = (Map<String, Object>)allProcessedModels.get(modelName);
try {
//不需要生成importMapping中有的类
if(config.importMapping().containsKey(modelName)) {
continue;
}
//获取模板
Map<String, Object> modelTemplate = (Map<String, Object>) ((List<Object>) models.get("models")).get(0);
if (isJavaCodegen(config.getName())) {
// 特殊处理java别名
if (modelTemplate != null && modelTemplate.containsKey("model")) {
CodegenModel codegenModel = (CodegenModel) modelTemplate.get("model");
Map<String, Object> vendorExtensions = codegenModel.getVendorExtensions();
boolean isAlias = false;
if (vendorExtensions.get(CodegenConstants.IS_ALIAS_EXT_NAME) != null) {
isAlias = Boolean.parseBoolean(vendorExtensions.get(CodegenConstants.IS_ALIAS_EXT_NAME).toString());
}
if (isAlias) {
continue;
}
}
}
allModels.add(modelTemplate);
for (String templateName : config.modelTemplateFiles().keySet()) {
String suffix = config.modelTemplateFiles().get(templateName);
String filename = config.modelFileFolder() + File.separator + config.toModelFilename(modelName) + suffix;
if (!config.shouldOverwrite(filename)) {
LOGGER.info("Skipped overwriting " + filename);
continue;
}
//根据模板生成代码
File written = processTemplateToFile(models, templateName, filename);
if(written != null) {
files.add(written);
}
}
if(generateModelTests) {
//生成测试代码
generateModelTests(files, models, modelName);
}
if(generateModelDocumentation) {
// 生成doc
generateModelDocumentation(files, models, modelName);
}
} catch (Exception e) {
throw new RuntimeException("Could not generate model '" + modelName + "'", e);
}
}
......
}
private void generateApis(List<File> files, List<Object> allOperations, List<Object> allModels) {
if (!generateApis) {
return;
}
boolean hasModel = true;
if (allModels == null || allModels.isEmpty()) {
hasModel = false;
}
//获取所有整理过的路径信息
Map<String, List<CodegenOperation>> paths = processPaths(this.openAPI.getPaths());
Set<String> apisToGenerate = null;
String apiNames = System.getProperty("apis");
if(apiNames != null && !apiNames.isEmpty()) {
apisToGenerate = new HashSet<String>(Arrays.asList(apiNames.split(",")));
}
if(apisToGenerate != null && !apisToGenerate.isEmpty()) {
Map<String, List<CodegenOperation>> updatedPaths = new TreeMap<>();
for(String m : paths.keySet()) {
if(apisToGenerate.contains(m)) {
updatedPaths.put(m, paths.get(m));
}
}
paths = updatedPaths;
}
//生成每个api类
for (String tag : paths.keySet()) {
try {
List<CodegenOperation> ops = paths.get(tag);
Collections.sort(ops, new Comparator<CodegenOperation>() {
@Override
public int compare(CodegenOperation one, CodegenOperation another) {
return ObjectUtils.compare(one.operationId, another.operationId);
}
});
//获取api代码所需要的所有信息
Map<String, Object> operation = processOperations(config, tag, ops, allModels);
//路径对应OpenAPI的server的url
operation.put("basePath", basePath);
//没有host的路径
operation.put("basePathWithoutHost", basePathWithoutHost);
operation.put("contextPath", contextPath);
//标签名字,用于关联path(operation)
operation.put("baseName", tag);
//model包路径
operation.put("modelPackage", config.modelPackage());
operation.putAll(config.additionalProperties());
//类名 toApiName会生成驼峰格式的类名
operation.put("classname", config.toApiName(tag));
operation.put("classVarName", config.toApiVarName(tag));
operation.put("importPath", config.toApiImport(tag));
//类的文件名
operation.put("classFilename", config.toApiFilename(tag));
//额外的信息(可以包含自定义信息)
if(!config.vendorExtensions().isEmpty()) {
operation.put("vendorExtensions", config.vendorExtensions());
}
// Pass sortParamsByRequiredFlag through to the Mustache template...
boolean sortParamsByRequiredFlag = true;
if (this.config.additionalProperties().containsKey(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)) {
sortParamsByRequiredFlag = Boolean.valueOf(this.config.additionalProperties().get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString());
}
operation.put("sortParamsByRequiredFlag", sortParamsByRequiredFlag);
operation.put("hasModel", hasModel);
allOperations.add(new HashMap<>(operation));
for (int i = 0; i < allOperations.size(); i++) {
Map<String, Object> oo = (Map<String, Object>) allOperations.get(i);
if (i < (allOperations.size() - 1)) {
oo.put("hasMore", "true");
}
}
for (String templateName : config.apiTemplateFiles().keySet()) {
String filename = config.apiFilename(templateName, tag);
if (!config.shouldOverwrite(filename) && new File(filename).exists()) {
LOGGER.info("Skipped overwriting " + filename);
continue;
}
//根据模板生成代码
File written = processTemplateToFile(operation, templateName, filename);
if(written != null) {
files.add(written);
}
}
if(generateApiTests) {
// 生成api测试文件
for (String templateName : config.apiTestTemplateFiles().keySet()) {
String filename = config.apiTestFilename(templateName, tag);
// do not overwrite test file that already exists
if (new File(filename).exists()) {
LOGGER.info("File exists. Skipped overwriting " + filename);
continue;
}
File written = processTemplateToFile(operation, templateName, filename);
if (written != null) {
files.add(written);
}
}
}
if(generateApiDocumentation) {
// 生成api doc
for (String templateName : config.apiDocTemplateFiles().keySet()) {
String filename = config.apiDocFilename(templateName, tag);
if (!config.shouldOverwrite(filename) && new File(filename).exists()) {
LOGGER.info("Skipped overwriting " + filename);
continue;
}
File written = processTemplateToFile(operation, templateName, filename);
if (written != null) {
files.add(written);
}
}
}
} catch (Exception e) {
throw new RuntimeException("Could not generate api file for '" + tag + "'", e);
}
}
......
}
private void generateSupportingFiles(List<File> files, Map<String, Object> bundle) {
if (!generateSupportingFiles) {
return;
}
Set<String> supportingFilesToGenerate = null;
String supportingFiles = System.getProperty(CodegenConstants.SUPPORTING_FILES);
boolean generateAll = false;
if (supportingFiles != null && supportingFiles.equalsIgnoreCase("true")) {
generateAll = true;
} else if (supportingFiles != null && !supportingFiles.isEmpty()) {
supportingFilesToGenerate = new HashSet<>(Arrays.asList(supportingFiles.split(",")));
}
for (SupportingFile support : config.supportingFiles()) {
try {
//生成文件和模板名字
......
//根据模板生成支持文件
if(ignoreProcessor.allowsFile(new File(outputFilename))) {
if (templateFile.endsWith("mustache")) {
String rendered = templateEngine.getRendered(templateFile, bundle);
writeToFile(outputFilename, rendered);
files.add(new File(outputFilename));
} else {
InputStream in = null;
try {
in = new FileInputStream(templateFile);
} catch (Exception e) {
// continue
}
if (in == null) {
in = this.getClass().getClassLoader().getResourceAsStream(getCPResourcePath(templateFile));
}
File outputFile = new File(outputFilename);
OutputStream out = new FileOutputStream(outputFile, false);
if (in != null) {
LOGGER.info("writing file " + outputFile);
IOUtils.copy(in, out);
out.close();
} else {
LOGGER.warn("can't open " + templateFile + " for input");
}
files.add(outputFile);
}
} else {
LOGGER.info("Skipped generation of " + outputFilename + " due to rule in .swagger-codegen-ignore");
}
} catch (Exception e) {
throw new RuntimeException("Could not generate supporting file '" + support + "'", e);
}
}
//需要则生成 .swagger-codegen-ignore
final String swaggerCodegenIgnore = ".swagger-codegen-ignore";
String ignoreFileNameTarget = config.outputFolder() + File.separator + swaggerCodegenIgnore;
File ignoreFile = new File(ignoreFileNameTarget);
if (generateSwaggerMetadata && !ignoreFile.exists()) {
String ignoreFileNameSource = File.separator + config.getCommonTemplateDir() + File.separator + swaggerCodegenIgnore;
String ignoreFileContents = readResourceContents(ignoreFileNameSource);
try {
writeToFile(ignoreFileNameTarget, ignoreFileContents);
} catch (IOException e) {
throw new RuntimeException("Could not generate supporting file '" + swaggerCodegenIgnore + "'", e);
}
files.add(ignoreFile);
}
if(generateSwaggerMetadata) {
//生成swagger元数据
final String swaggerVersionMetadata = config.outputFolder() + File.separator + ".swagger-codegen" + File.separator + "VERSION";
File swaggerVersionMetadataFile = new File(swaggerVersionMetadata);
try {
writeToFile(swaggerVersionMetadata, ImplementationVersion.read());
files.add(swaggerVersionMetadataFile);
} catch (IOException e) {
throw new RuntimeException("Could not generate supporting file '" + swaggerVersionMetadata + "'", e);
}
}
}