Swagger Codegen 源码分析
准备
从 Github 获取源码:
git clone https://github.com/swagger-api/swagger-codegen.git
项目结构:
- 模板位置:
modules/swagger-codegen/src/main/resources
- 程序入口:
modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/SwaggerCodegen.java
运行程序:
generate -l java -i "E:\code\swagger-codegen\api.yaml" -o "E:\temp\out" -c "E:\code\swagger-codegen\config.json"
源码分析
程序入口
使用 Airline 解析命令行参数。
当我们传递 generate -l java -i api.yaml -o "E:\temp\out"
时,parse()
会通过反射创建 io.swagger.codegen.cmd.Generate
类实例。
// io.swagger.codegen.SwaggerCodegen
public class SwaggerCodegen {
public static void main(String[] args) {
String version = Version.readVersionFromResources();
// 类型参数表明 parse() 方法返回的类型
@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)
.withCommands(Generate.class, Meta.class, Langs.class, Help.class,
ConfigHelp.class, Validate.class, Version.class);
builder.build().parse(args).run();
}
}
// io.airlift.airline.DefaultCommandFactory
public class DefaultCommandFactory<T> implements CommandFactory<T> {
@SuppressWarnings("unchecked")
@Override
public T createInstance(Class<?> type) {
return (T) ParserUtil.createInstance(type); // type: io.swagger.codegen.cmd.Generate
}
}
命令类
Generate 源码分析
详见 Generate.java:
@Command(name = "generate", description = "Generate code with chosen lang")
public class Generate implements Runnable {
@Option(name = {"-v", "--verbose"})
private Boolean verbose;
@Option(name = {"-l", "--lang"}, title = "language", required = true)
private String lang;
// ... 省略了其他保存参数的属性
@Override
public void run() {
// 读取 -c conifg.json 文件
// attempt to read from config file
CodegenConfigurator configurator = CodegenConfigurator.fromFile(configFile);
// if a config file wasn't specified or we were unable to read it
if (configurator == null) {
// createa a fresh configurator
configurator = new CodegenConfigurator();
}
// 使用命令行参数覆盖 config.json 中的配置
// now override with any specified parameters
if (verbose != null) {
configurator.setVerbose(verbose);
}
if (skipOverwrite != null) {
configurator.setSkipOverwrite(skipOverwrite);
}
// ... 省略了其他覆盖配置的代码
if (ignoreImportMappings != null) {
additionalProperties.add(String.format("%s=%s", CodegenConstants.IGNORE_IMPORT_MAPPING_OPTION, Boolean.parseBoolean(ignoreImportMappings)));
}
// 这些也是将参数加到 configurator 中
applySystemPropertiesKvpList(systemProperties, configurator); // 系统属性
applyInstantiationTypesKvpList(instantiationTypes, configurator); // 在生成实例代码时使用的实现类,格式:swaggerType=instantionType,示例:array=ArrayList,map=HashMap
applyImportMappingsKvpList(importMappings, configurator); // 类和它的导入语句的映射关系,格式:javaType=import, 示例:LocalDate=java.time.LocalDate,LocalDateTime=java.util.LocalDateTime
applyTypeMappingsKvpList(typeMappings, configurator); // Swagger 类型和 语言类型映射关系,格式:swaggerType=javaType,示例:array=List,map=Map,string=String
applyAdditionalPropertiesKvpList(additionalProperties, configurator);
applyLanguageSpecificPrimitivesCsvList(languageSpecificPrimitives, configurator); // 指定的类型作为原始数据类型,下面会详解
applyReservedWordsMappingsKvpList(reservedWordsMappings, configurator);
final ClientOptInput clientOptInput = configurator.toClientOptInput(); // 读取 OpenAPI 文档
new DefaultGenerator().opts(clientOptInput).generate(); // 调用 DefaultGenerator.generate() 生成代码文件
}
}
使用已有的数据模型而不自动生成该模型
这篇文章 OpenAPI Generator - target external models 有解释 --language-specific-primitives
的作用,但 Swagger CodeGen 的实现似乎没有使用到这个参数来跳过模型生成。该参数的意思是将一些类型加入原始类型列表,这样生成器就不会为其生成数据模型代码。
如果我们想使用一些已有的数据模型而且不想 Swagger CodeGen 为我们生成,可以这样配置:
--ignore-import-mapping=false --import-mappings=Pet=com.xxx.model.Pet
也可以通过设置这个扩展字段跳过模型生成:
// io.swagger.codegen.DefaultGenerator#processModels
protected Map<String, Object> processModels(CodegenConfig config, Map<String, Model> definitions, Map<String, Model> allDefinitions) {
for (String key : definitions.keySet()) {
Model mm = definitions.get(key);
if(mm.getVendorExtensions() != null && mm.getVendorExtensions().containsKey("x-codegen-ignore")) {
// skip this model
LOGGER.debug("skipping model " + key);
return null;
}
}
}
相关源码:
// io.swagger.codegen.cmd.Generate#run
@Override
public void run() {
// attempt to read from config file
CodegenConfigurator configurator = CodegenConfigurator.fromFile(configFile);
// if a config file wasn't specified or we were unable to read it
if (configurator == null) {
// createa a fresh configurator
configurator = new CodegenConfigurator();
}
// ...
// now override with any specified parameters
// 设置 ignoreImportMapping
if (ignoreImportMappings != null) {
additionalProperties.add(String.format("%s=%s", CodegenConstants.IGNORE_IMPORT_MAPPING_OPTION, Boolean.parseBoolean(ignoreImportMappings)));
}
// ...
final ClientOptInput clientOptInput = configurator.toClientOptInput();
new DefaultGenerator().opts(clientOptInput).generate();
}
// io.swagger.codegen.DefaultGenerator#generateModels
protected void generateModels(List<File> files, List<Object> allModels) {
for (String name : modelKeys) {
//don't generate models that have an import mapping
// 设置不忽略 ImportMapping 并且 ImportMapping 存在该类
if (!config.getIgnoreImportMapping() && config.importMapping().containsKey(name)) {
LOGGER.info("Model " + name + " not imported due to import mapping");
continue;
}
}
}
读取 OpenAPI 文档
读取 OpenAPI 文档时,有个地方值得关注,就是读取方法参数类型信息。
// io.swagger.codegen.config.CodegenConfigurator#toClientOptInput
public ClientOptInput toClientOptInput() {
// 省略其他代码
// 读取 OpenAPI 文档并转换成 Swagger 对象
Swagger swagger = new SwaggerParser().read(inputSpec, authorizationValues, parseOptions);
input.opts(new ClientOpts())
.swagger(swagger);
}
// io.swagger.parser.SwaggerParser#read(java.lang.String, java.util.List<io.swagger.models.auth.AuthorizationValue>, io.swagger.parser.util.ParseOptions)
public Swagger read(String location, List<AuthorizationValue> auths, ParseOptions options) {
// ... 省略其他代码
Swagger output;
output = (new Swagger20Parser()).read(location, auths);
}
// io.swagger.parser.Swagger20Parser#read(java.lang.String, java.util.List<io.swagger.models.auth.AuthorizationValue>)
public Swagger read(String location, List<AuthorizationValue> auths) throws IOException {
// ...省略其他代码
// 从网络或本地读取文件
location = location.replaceAll("\\\\", "/");
String data;
if (location.toLowerCase().startsWith("http")) {
data = RemoteUrl.urlToString(location, auths);
} else {
Path pathpath = Paths.get(location);
}
if (Files.exists(path, new LinkOption[0])) {
data = FileUtils.readFileToString(path.toFile(), "UTF-8");
} else {
data = ClasspathHelper.loadFileFromClasspath(location);
}
return this.convertToSwagger(data); // 将文件读取成字符串并转换成 Swagger 对象
}
一路上弯弯绕绕:
将 JSON 或 YAML 文件解析成字符串:
// io.swagger.parser.Swagger20Parser#convertToSwagger
private Swagger convertToSwagger(String data) throws IOException {
// 注:JSON 和 YAML 的关系很密切,YAML 可以理解为 JSON 的超集
JsonNode rootNode;
if (data.trim().startsWith("{")) {
// 解析 JSON 字符串
ObjectMapper mapper = Json.mapper();
rootNode = mapper.readTree(data);
} else {
// 解析 YAML 字符串
rootNode = this.deserializeYaml(data);
}
JsonNode swaggerNode = rootNode.get("swagger");
SwaggerDeserializationResult result = (new SwaggerDeserializer()).deserialize(rootNode); // 反序列化 JSON 对象
Swagger convertValue = result.getSwagger();
return convertValue;
}
// io.swagger.parser.util.SwaggerDeserializer#deserialize
public SwaggerDeserializationResult deserialize(JsonNode rootNode) {
SwaggerDeserializationResult result = new SwaggerDeserializationResult();
SwaggerDeserializer.ParseResult rootParse = new SwaggerDeserializer.ParseResult();
Swagger swagger = this.parseRoot(rootNode, rootParse); // 解析 JSON 对象
result.setSwagger(swagger);
result.setMessages(rootParse.getMessages());
return result;
}
解析 OpenAPI 文档:
// io.swagger.parser.util.SwaggerDeserializer#parseRoot
// 这个方法就是主要的解析方法,解析 OpenAPI 文档的所有内容,包括 paths, definitions...
public Swagger parseRoot(JsonNode node, SwaggerDeserializer.ParseResult result) {
String location = "";
Swagger swagger = new Swagger();
// 解析接口方法、包括参数类型
obj = this.getObject("paths", on, true, location, result);
Map<String, Path> paths = this.paths(obj, "paths", result); // 这里会解析接口方法
swagger.paths(paths);
// 解析模型定义
obj = this.getObject("definitions", on, false, location, result);
Map<String, Model> definitions = this.definitions(obj, "definitions", result);
swagger.setDefinitions(definitions);
// 解析参数对象定义,注意要去接口方法的参数类型区分,详见 OpenAPI 规范
obj = this.getObject("parameters", on, false, location, result);
Map<String, Parameter> parameters = new LinkedHashMap();
swagger.setParameters(parameters);
}
解析 paths 定义:
// io.swagger.parser.util.SwaggerDeserializer#paths
public Map<String, Path> paths(ObjectNode obj, String location, SwaggerDeserializer.ParseResult result) {
Set<String> pathKeys = this.getKeys(obj);
Iterator var6 = pathKeys.iterator();
while(true) {
while(var6.hasNext()) {
String pathName = (String)var6.next();
JsonNode pathValue = obj.get(pathName);
ObjectNode path = (ObjectNode)pathValue;
Path pathObj = this.path(path, location + ".'" + pathName + "'", result); // 遍历解析接口方法
List<String> eachPart = new ArrayList();
Matcher m = Pattern.compile("\\{(.+?)\\}").matcher(pathName);
}
return output;
}
}
解析 path 定义:
// io.swagger.parser.util.SwaggerDeserializer#path
public Path path(ObjectNode obj, String location, SwaggerDeserializer.ParseResult result) {
Path path = new Path();
// 这里是解析参数对象定义,注意要去接口方法的参数类型区分,详见 OpenAPI 规范
ArrayNode parameters = this.getArray("parameters", obj, false, location, result);
path.setParameters(this.parameters(parameters, location, result));
ObjectNode on = this.getObject("get", obj, false, location, result);
Operation op;
// 按 HTTP 方法类型去解析接口方法
if (on != null) {
op = this.operation(on, location + "(get)", result); // 解析路径下的 GET 请求接口
if (op != null) {
path.setGet(op);
}
}
on = this.getObject("post", obj, false, location, result);
if (on != null) {
op = this.operation(on, location + "(post)", result);
if (op != null) {
path.setPost(op);
}
}
// ... 省略了其他 HTTP 方法类型
return path;
}
解析接口方法:
// io.swagger.parser.util.SwaggerDeserializer#operation
// 这个方法是解析接口方法的主要方法
public Operation operation(ObjectNode obj, String location, SwaggerDeserializer.ParseResult result) {
// ... 省略了其他代码
Operation output = new Operation();
// 解析接口参数
ArrayNode parameters = this.getArray("parameters", obj, false, location, result);
output.setParameters(this.parameters(parameters, location, result)); // 解析并设置方法参数
return output;
}
解析接口方法参数:
// io.swagger.parser.util.SwaggerDeserializer#parameters
public List<Parameter> parameters(ArrayNode obj, String location, SwaggerDeserializer.ParseResult result) {
List<Parameter> output = new ArrayList();
Iterator var5 = obj.iterator();
while(var5.hasNext()) {
JsonNode item = (JsonNode)var5.next();
if (item.getNodeType().equals(JsonNodeType.OBJECT)) {
Parameter param = this.parameter((ObjectNode)item, location, result); // 遍历并解析接口方法参数
if (param != null) {
output.add(param);
}
}
}
return output;
}
终于来到解析参数类型的地方,请注意注释中的【关键】:
// io.swagger.parser.util.SwaggerDeserializer#parameter
// 解析单个接口方法参数
public Parameter parameter(ObjectNode obj, String location, SwaggerDeserializer.ParseResult result) {
String value = this.getString("in", obj, true, location, result);
if (value != null) {
// 注:in 的含义详见 OpenAPI 规范
// 如果 in 的值是 query, header, path, formatData,则直接读取 type, format 的值
String type = this.getString("type", obj, false, location, result);
String format = this.getString("format", obj, false, location, result);
AbstractSerializableParameter<?> sp = null;
if ("query".equals(value)) {
sp = new QueryParameter();
} else if ("header".equals(value)) {
sp = new HeaderParameter();
} else if ("path".equals(value)) {
sp = new PathParameter();
} else if ("formData".equals(value)) {
sp = new FormParameter();
}
if (sp != null) {
String paramType = this.getString("type", obj, true, location, result);
Map<PropertyId, Object> map = new LinkedHashMap();
map.put(PropertyId.TYPE, type);
map.put(PropertyId.FORMAT, format);
Property prop = PropertyBuilder.build(type, format, map); // 关键:弯弯绕绕终于来到这里,这里将 OpenAPI 定义的类型字符串转为 Java 对象。后面生成日期,Map 相关代码时,会用到这个对象。
if (prop != null) {
((AbstractSerializableParameter)sp).setProperty(prop);
ObjectNode items = this.getObject("items", obj, false, location, result);
if (items != null) {
Property inner = this.schema((Map)null, items, location, result);
((AbstractSerializableParameter)sp).setItems(inner);
}
}
String collectionFormat = this.getString("collectionFormat", obj, false, location, result);
((AbstractSerializableParameter)sp).setCollectionFormat(collectionFormat);
output = sp;
} else if ("body".equals(value)) {
// 如果 in 的值是 body,则需要读取 schmea 定义
BodyParameter bp = new BodyParameter();
JsonNode node = obj.get("schema");
if (node != null && node instanceof ObjectNode) {
bp.setSchema(this.definition((ObjectNode)node, location, result));
}
output = bp;
}
return (Parameter)output;
}
Swagger 数据类型:
type | format | Comments |
---|---|---|
integer | int32 | signed 32 bits |
integer | int64 | signed 64 bits (a.k.a long) |
number | float | |
number | double | |
string | ||
string | byte | base64 encoded characters |
string | binary | any sequence of octets |
boolean | ||
string | date | As defined by full-date - RFC3339 |
string | date-time | As defined by date-time - RFC3339 |
string | password | A hint to UIs to obscure input. |
// io.swagger.models.properties.PropertyBuilder.Processor#fromType
// 这里的 Processor 是一个枚举类型,表示 Swagger 数据类型
public static Property build(String type, String format, Map<PropertyId, Object> args) {
final Processor processor = Processor.fromType(type, format);
return processor.build(fixedArgs);
}
这里列举了一些 Property 的实现类,可以看到其 OpenAPI 规范的对应书写方式,即 type 和 format 的值:
// io.swagger.models.properties.DateProperty
public class DateProperty extends AbstractProperty implements Property {
public DateProperty() {
super.type = "string";
super.format = "date";
}
}
// io.swagger.models.properties.DateTimeProperty
public class DateTimeProperty extends AbstractProperty implements Property {
public DateTimeProperty() {
super.type = "string";
super.format = "date-time";
}
}
如何生成 LocalDate 和 LocalDateTime
OpenAPI 文档:
paths:
/example:
get:
operationId: "example"
parameters:
- in: "query"
name: arg0
type: string
format: date
- in: 'query'
name: arg1
type: string
format: date-time
responses:
"200":
description: "OK"
配置:
--additional-properties="dateLibrary=java8-localdatetime"
相关源码:
// io.swagger.codegen.DefaultGenerator#generate
@Override
public List<File> generate() {
// ...
configureGeneratorProperties();
// ...
}
// io.swagger.codegen.DefaultGenerator#configureGeneratorProperties
protected void configureGeneratorProperties() {
// ...
config.processOpts();
// ...
}
// io.swagger.codegen.languages.SpringCodegen#processOpts
@Override
public void processOpts() {
// Process java8 option before common java ones to change the default dateLibrary to java8.
if (additionalProperties.containsKey(JAVA_8)) { // java8
this.setJava8(Boolean.valueOf(additionalProperties.get(JAVA_8).toString()));
}
if (this.java8) {
additionalProperties.put("javaVersion", "1.8");
additionalProperties.put("jdk8", "true");
if (!additionalProperties.containsKey(DATE_LIBRARY)) { // dateLibrary
setDateLibrary("java8");
}
}
// ...
super.processOpts(); // 调用 AbstractJavaCodegen#processOpts
// ...
}
根据参数选择日期类型:
// io.swagger.codegen.languages.AbstractJavaCodegen#processOpts
@Override
public void processOpts() {
super.processOpts();
// ...
if (additionalProperties.containsKey(DATE_LIBRARY)) {
setDateLibrary(additionalProperties.get("dateLibrary").toString());
} else if (java8Mode) {
setDateLibrary("java8");
}
if ("threetenbp".equals(dateLibrary)) {
additionalProperties.put("threetenbp", true);
additionalProperties.put("jsr310", "true");
typeMapping.put("date", "LocalDate");
typeMapping.put("DateTime", "OffsetDateTime");
importMapping.put("LocalDate", "org.threeten.bp.LocalDate");
importMapping.put("OffsetDateTime", "org.threeten.bp.OffsetDateTime");
} else if ("joda".equals(dateLibrary)) {
additionalProperties.put("joda", true);
typeMapping.put("date", "LocalDate");
typeMapping.put("DateTime", "DateTime");
importMapping.put("LocalDate", "org.joda.time.LocalDate");
importMapping.put("DateTime", "org.joda.time.DateTime");
} else if (dateLibrary.startsWith("java8")) {
additionalProperties.put("java8", true);
additionalProperties.put("jsr310", "true");
if ("java8-localdatetime".equals(dateLibrary)) { // 选择该配置
typeMapping.put("date", "LocalDate");
typeMapping.put("DateTime", "LocalDateTime");
importMapping.put("LocalDate", "java.time.LocalDate");
importMapping.put("LocalDateTime", "java.time.LocalDateTime");
} else if ("java8-instant".equals(dateLibrary)) {
typeMapping.put("date", "Instant");
typeMapping.put("DateTime", "Instant");
importMapping.put("Instant", "java.time.Instant");
} else {
typeMapping.put("date", "LocalDate");
typeMapping.put("DateTime", "OffsetDateTime");
importMapping.put("LocalDate", "java.time.LocalDate");
importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
}
} else if (dateLibrary.equals("legacy")) {
additionalProperties.put("legacyDates", true);
}
}
如何生成 Map
生成 Map<String, String>
类型:
paths:
/example:
get:
operationId: "example"
consumes:
- multipart/form-data
parameters:
- in: "formData"
name: arg0
type: object
items:
type: integer
format: int64
responses:
"200":
description: "example"
生成 Map<String, Pet>
类型,虽然这样写 Swagger 编辑器会报错,但确实只有这样写才会生成想要的代码,原因见下面源码分析:
paths:
/example:
get:
operationId: "example"
consumes:
- multipart/form-data
parameters:
- in: "formData"
name: arg0
type: object
items:
$ref: "#/definitions/Pet"
responses:
"200":
description: "example"
definitions:
Pet:
type: object
properties:
name:
type: string
相关源码:
解析 OpenAPI 文档:
// io.swagger.parser.util.SwaggerDeserializer#parameter
public Parameter parameter(ObjectNode obj, String location, SwaggerDeserializer.ParseResult result) {
Property prop = PropertyBuilder.build(type, format, map); // 解析参数类型
if (prop != null) {
((AbstractSerializableParameter)sp).setProperty(prop);
ObjectNode items = this.getObject("items", obj, false, location, result); // 解析 Map 第二个泛型参数,第一泛型参数默认是 String 类型。(可以看到这里读的是 OpenAPI 文档的 items 属性)
if (items != null) {
Property inner = this.schema((Map)null, items, location, result);
((AbstractSerializableParameter)sp).setItems(inner);
}
}
}
将 Swagger 类型转成 Java 类型:
// io.swagger.codegen.DefaultCodegen#fromParameter
// 该方法将 Swagger 类型转成 Java 类型
public CodegenParameter fromParameter(Parameter param, Set<String> imports) {
if (param instanceof SerializableParameter) { // SerializableParameter 不包括 Body,即请求体
SerializableParameter qp = (SerializableParameter) param;
Property property;
String type = qp.getType();
if ("array".equals(type)) { // for array parameter
// ...
} else if ("object".equals(type)) { // for map parameter
Property inner = qp.getItems(); // 这里取的是 items,作为 Map 的第二个泛型参数
if (inner == null) {
inner = new StringProperty().description("//TODO automatically added by swagger-codegen");
}
property = new MapProperty(inner); // 转成 Map 类型
collectionFormat = qp.getCollectionFormat();
CodegenProperty pr = fromProperty("inner", inner);
p.items = pr;
p.baseType = pr.datatype;
p.isContainer = true;
p.isMapContainer = true;
// recursively add import
while (pr != null) {
imports.add(pr.baseType);
pr = pr.items;
}
} else {
// ...
}
}
}
生成代码文件
接下来,看 DefaultGenerator.generate()
方法,详见:DefaultGenerator.java
@Override
public List<File> generate() {
if (swagger == null || config == null) {
throw new RuntimeException("missing swagger input or config!");
}
// 根据系统属性决定是否生成模型文件、接口文件、支持文件,并将这些信息加入 additionalProperties
configureGeneratorProperties();
// 将 OpenAPI 文件信息加入 additionalProperties
configureSwaggerInfo();
// 生成模型文件、接口文件、支持文件
List<File> files = new ArrayList<File>();
// models
List<Object> allModels = new ArrayList<Object>();
generateModels(files, allModels);
// apis
List<Object> allOperations = new ArrayList<Object>();
generateApis(files, allOperations, allModels);
// supporting files
Map<String, Object> bundle = buildSupportFileBundle(allOperations, allModels);
generateSupportingFiles(files, bundle);
config.processSwagger(swagger);
return files;
}
生成数据模型文件
生成数据模型文件:
- 如果系统属性配置了
models
,则只生成这些数据模型 - 对数据模型进行排序
- 如果定义的数据模型是可以导入的类型,则不生成这些数据模型文件
- 对数据模型进行处理
- 对于每个模型,都会遍历模型模板并生成响应的模型代码文件
- 生成测试文件(可选)
- 生成文档文件(可选)
protected void generateModels(List<File> files, List<Object> allModels) {
if (!isGenerateModels) {
return;
}
// OpenApi 定义的 Definitions (数据模型)
final Map<String, Model> definitions = swagger.getDefinitions();
if (definitions == null) {
return;
}
// 如果指定了系统属性 models,则只生成这些数据模型,将要生成的数据模型放到 modelKeys 中
String modelNames = System.getProperty("models");
Set<String> modelsToGenerate = null;
if (modelNames != null && !modelNames.isEmpty()) {
modelsToGenerate = new HashSet<String>(Arrays.asList(modelNames.split(",")));
}
Set<String> modelKeys = definitions.keySet();
if (modelsToGenerate != null && !modelsToGenerate.isEmpty()) {
Set<String> updatedKeys = new HashSet<String>();
for (String m : modelKeys) {
if (modelsToGenerate.contains(m)) {
updatedKeys.add(m);
}
}
modelKeys = updatedKeys;
}
// 对数据模型进行排序
// store all processed models
Map<String, Object> allProcessedModels = new TreeMap<String, Object>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
Model model1 = definitions.get(o1);
Model model2 = definitions.get(o2);
// ...
});
// process models only
for (String name : modelKeys) {
try {
// 如果启用 ImportMapping 并且 ImportMapping 包含该类型,说明可以直接导入,则不生成该数据模型
//don't generate models that have an import mapping
if (!config.getIgnoreImportMapping() && config.importMapping().containsKey(name)) {
LOGGER.info("Model " + name + " not imported due to import mapping");
continue;
}
Model model = definitions.get(name);
Map<String, Model> modelMap = new HashMap<String, Model>();
modelMap.put(name, model);
// 对数据模型进行处理,比如是否忽略该模型和添加一些信息
Map<String, Object> models = processModels(config, modelMap, definitions);
if (models != null) {
models.put("classname", config.toModelName(name));
models.putAll(config.additionalProperties());
// 加入 allProcessedModels 的数据模型才会生成代码文件
allProcessedModels.put(name, models);
}
} catch (Exception e) {
String message = "Could not process model '" + name + "'" + ". Please make sure that your schema is correct!";
LOGGER.error(message, e);
throw new RuntimeException(message, e);
}
}
// post process all processed models
allProcessedModels = config.postProcessAllModels(allProcessedModels);
final boolean skipAlias = config.getSkipAliasGeneration() != null && config.getSkipAliasGeneration();
// 生成数据模型文件
// generate files based on processed models
for (String modelName : allProcessedModels.keySet()) {
Map<String, Object> models = (Map<String, Object>) allProcessedModels.get(modelName);
models.put("modelPackage", config.modelPackage());
try {
//don't generate models that have an import mapping
if (!config.getIgnoreImportMapping() && config.importMapping().containsKey(modelName)) {
continue;
}
Map<String, Object> modelTemplate = (Map<String, Object>) ((List<Object>) models.get("models")).get(0);
if (skipAlias) {
// Special handling of aliases only applies to Java
if (modelTemplate != null && modelTemplate.containsKey("model")) {
CodegenModel m = (CodegenModel) modelTemplate.get("model");
if (m.isAlias) {
continue; // Don't create user-defined classes for aliases
}
}
}
allModels.add(modelTemplate);
// 遍历模型模板文件并生成对应的模型代码文件
for (String templateName : config.modelTemplateFiles().keySet()) {
// 获取文件名
String filename = config.modelFilename(templateName, modelName);
if (!config.shouldOverwrite(filename)) {
LOGGER.info("Skipped overwriting " + filename);
continue;
}
// 生成代码文件
File written = processTemplateToFile(models, templateName, filename);
if (written != null) {
files.add(written);
}
}
// 生成测试文件
if(isGenerateModelTests) {
generateModelTests(files, models, modelName);
}
// 生成文档文件
if(isGenerateModelDocumentation) {
// to generate model documentation files
generateModelDocumentation(files, models, modelName);
}
} catch (Exception e) {
throw new RuntimeException("Could not generate model '" + modelName + "'", e);
}
}
if (System.getProperty("debugModels") != null) {
LOGGER.info("############ Model info ############");
Json.prettyPrint(allModels);
}
}
生成接口文件
生成接口文件:
- 对接口进行分组,默认是按接口的 tags 属性分组,spring 可以选择按路径的第一段分组
- 如果指定了系统属性 apis,只这生成这些接口
- 遍历每个分组并生成接口文件
protected void generateApis(List<File> files, List<Object> allOperations, List<Object> allModels) {
if (!isGenerateApis) {
return;
}
// 对接口进行分组,默认是按接口的 tags 属性分组,spring 可以选择按路径的第一段分组
Map<String, List<CodegenOperation>> paths = processPaths(swagger.getPaths());
// 如果指定了系统属性 apis,只这生成这些接口
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<String, List<CodegenOperation>>();
for (String m : paths.keySet()) {
if (apisToGenerate.contains(m)) {
updatedPaths.put(m, paths.get(m));
}
}
paths = updatedPaths;
}
// 遍历每个分组,并生成接口文件
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);
}
});
Map<String, Object> operation = processOperations(config, tag, ops, allModels);
operation.put("hostWithoutBasePath", getHostWithoutBasePath());
operation.put("basePath", basePath);
operation.put("basePathWithoutHost", basePathWithoutHost);
operation.put("contextPath", contextPath);
operation.put("baseName", tag);
operation.put("apiPackage", config.apiPackage());
operation.put("modelPackage", config.modelPackage());
operation.putAll(config.additionalProperties());
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);
processMimeTypes(swagger.getConsumes(), operation, "consumes");
processMimeTypes(swagger.getProduces(), operation, "produces");
allOperations.add(new HashMap<String, Object>(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(isGenerateApiTests) {
// to generate api test files
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(isGenerateApiDocumentation) {
// to generate api documentation files
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);
}
}
if (System.getProperty("debugOperations") != null) {
LOGGER.info("############ Operation info ############");
Json.prettyPrint(allOperations);
}
}
对接口方法进行分组的代码:
public Map<String, List<CodegenOperation>> processPaths(Map<String, Path> paths) {
Map<String, List<CodegenOperation>> ops = new TreeMap<String, List<CodegenOperation>>();
for (String resourcePath : paths.keySet()) {
Path path = paths.get(resourcePath);
processOperation(resourcePath, "get", path.getGet(), ops, path);
processOperation(resourcePath, "post", path.getPost(), ops, path);
// ...
}
return ops;
}
protected void processOperation(String resourcePath, String httpMethod, Operation operation, Map<String, List<CodegenOperation>> operations, Path path) {
config.addOperationToGroup(config.sanitizeTag(tag.getName()), resourcePath, operation, codegenOperation, operations);
}
// SpringCodegen.java
public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map<String, List<CodegenOperation>> operations) {
// useTags == false 按路径第一段进行分组
if((library.equals(DEFAULT_LIBRARY) || library.equals(SPRING_MVC_LIBRARY)) && !useTags) {
String basePath = resourcePath;
if (basePath.startsWith("/")) {
basePath = basePath.substring(1);
}
int pos = basePath.indexOf("/");
if (pos > 0) {
basePath = basePath.substring(0, pos);
}
if (basePath.equals("")) {
basePath = "default";
} else {
co.subresourceOperation = !co.path.isEmpty();
}
List<CodegenOperation> opList = operations.get(basePath);
if (opList == null) {
opList = new ArrayList<CodegenOperation>();
operations.put(basePath, opList);
}
opList.add(co);
co.baseName = basePath;
} else {
// 默认按 tags 进行分组
super.addOperationToGroup(tag, resourcePath, operation, co, operations);
}
}