环境:jdk1.8+,spring boot(2.1.3.RELEASE),springfox-swagger2(2.5.0),springfox-swagger-ui(2.5.0)
最近使用springfox-swagger2。本来好好的接口,有一天突然发现swagger显示的接口参数A跟原来不一致,接口参数的描述变成了另外一个类A1的描述。检查后发现接口没变。但是别的同事增加了另外一个接口,而接口中定义了一个同样类名的对象,即A跟A1的类名相同。导致springfox-swagger2在生成swagger对象时,ModelMapper在合并Model时类名相同的model会被覆盖。主要是因为DefaultModelProvider在生成model时id和name设置成了类名,存储在map中的key使用的是类名。
DefaultModelProvider会调用TypeNameExtractor.typeName.
进一步调用TypeNameProviderPlugin extends Plugin
DefaultModelProvider
private Model modelBuilder(ResolvedType propertiesHost,
Map properties,
ModelContext modelContext) {
String typeName = typeNameExtractor.typeName(ModelContext.fromParent(modelContext, propertiesHost));
modelContext.getBuilder()
.id(typeName)
.type(propertiesHost)
.name(typeName)
.qualifiedType(simpleQualifiedTypeName(propertiesHost))
.properties(properties)
.description("")
.baseModel("")
.discriminator("")
.subTypes(new ArrayList());
return schemaPluginsManager.model(modelContext);
}
@Component
public class TypeNameExtractor {
private final TypeResolver typeResolver;
private final PluginRegistry typeNameProviders;
@Autowired
public TypeNameExtractor(TypeResolver typeResolver,
@Qualifier("typeNameProviderPluginRegistry")
PluginRegistry typeNameProviders) {
this.typeResolver = typeResolver;
this.typeNameProviders = typeNameProviders;
}
public String typeName(ModelContext context) {
ResolvedType type = asResolved(context.getType());
if (isContainerType(type)) {
return containerType(type);
}
return innerTypeName(type, context);
}
private ResolvedType asResolved(Type type) {
return typeResolver.resolve(type);
}
private String genericTypeName(ResolvedType resolvedType, ModelContext context) {
......
}
private String innerTypeName(ResolvedType type, ModelContext context) {
if (type.getTypeParameters().size() > 0 && type.getErasedType().getTypeParameters().length > 0) {
return genericTypeName(type, context);
}
return simpleTypeName(type, context);
}
private String simpleTypeName(ResolvedType type, ModelContext context) {
......
return typeName(new ModelNameContext(type.getErasedType(), context.getDocumentationType()));
}
private String typeName(ModelNameContext context) {
TypeNameProviderPlugin selected =
typeNameProviders.getPluginFor(context.getDocumentationType(), new DefaultTypeNameProvider());
return selected.nameFor(context.getType());
}
}
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
public class ApiModelTypeNameProvider extends DefaultTypeNameProvider {
@Override
public String nameFor(Class> type) {
ApiModel annotation = findAnnotation(type, ApiModel.class);
String defaultTypeName = super.nameFor(type);
if (annotation != null) {
return fromNullable(emptyToNull(annotation.value())).or(defaultTypeName);
}
return defaultTypeName;
}
@Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
public class DefaultTypeNameProvider implements TypeNameProviderPlugin {
@Override
public boolean supports(DocumentationType delimiter) {
return true;
}
@Override
public String nameFor(Class> type) {
return type.getSimpleName();
}
}
ModelMapper
Map modelsFromApiListings(Multimap apiListings) {
Map definitions = newHashMap();
for (ApiListing each : apiListings.values()) {
definitions.putAll(each.getModels());
}
return mapModels(definitions);
}
简单处理是修改类名,保证不存在相同的类名。
还可以在每个类名上添加注解@ApiModel("xxxxx").这样在生成model名时(使用ApiModelTypeNameProvider)使用配置的xxxxx.
从源码分析,似乎springfox-swagger2也是提供了很多可扩展点。待以后有时间再研究