preface

The [email protected] version used by the project

Integrating swagger document functionality in Spring requires the @APIModel annotation to modify the entry and exit classes, but if there are two classes in different packages with the same name that use the @APIModel annotation, the document will be overwritten, for example:

  • com.example.demo.login.dto.UserDTO
package com.example.demo.login.dto;

@Data
@ApiModel
public class UserDTO{
  @ ApiModelProperty (" name ")
  private String name;
  @ ApiModelProperty (" age ")
  private Integer age;
}
Copy the code
  • com.example.demo.vip.dto.UserDTO
package com.example.demo.vip.dto;

@Data
@ApiModel
public class UserDTO{
  @ ApiModelProperty (" name ")
  private String name;
  @APIModelProperty (" Membership level ")
  private Integer vipLevel;
}
Copy the code

The document generated by the above two classes becomes a Swagger Model:

Interface document display error:

Resolve the conflict

Modify @apiModel annotations (recommended)

To avoid the conflict of the same name, modify the value attribute of @APIModel.

package com.example.demo.login.dto;

@Data
@ApiModel("login$UserDTO")
public class UserDTO{}
Copy the code
package com.example.demo.vip.dto;

@Data
@ApiModel("vip$UserDTO")
public class UserDTO{}
Copy the code

We can see that two Swagger models are generated:

Modify the name of the class

Modify the two class names so that the class names do not conflict.

Custom Swagger plug-in

The solution to the conflict, however, was too much trouble, define a document access and class, consider the problem of class name repetition, the problem of the increase mental burden and workload should try to avoid the lost, I wonder if there may be only need to add on each class @ ApiModel annotations, all need not consider the rest of the conflict.

Thus, by tracing the source code, WE found the place where swagger Model name is generated, for details, see github

DefaultTypeName = @apiModel; defaultTypeName = @apiModel;

It is because the abbreviation of the class is taken by default that the Swagger Model generated by classes with the same name under different package names is overwritten. Now that the reason has been analyzed, the next step is to see if we can customize the super.namefor (type) method. Unfortunately, this method is dead and there is no place to start. However, the @Component and @Order annotations on the ApiModelTypeNameProvider class make it clear that this is a Spring bean and is loaded via the Spring plugin mechanism, so you can customize a plugin to do this. By default, generate a unique Swagger model with the full classpath and class name as follows:

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER - 100)
public class FullPathTypeNameProvider extends DefaultTypeNameProvider {

    public static final String SPLIT_CHAR = "$";

    @Override
    public String nameFor(Class
        type) {
        ApiModel annotation = AnnotationUtils.findAnnotation(type, ApiModel.class);
        if (annotation == null) {
            return super.nameFor(type);
        }
        if (StringUtils.hasText(annotation.value())) {
            return annotation.value();
        }
        // If @apiModel value is empty, the full classpath is taken by default
        int packagePathLength = type.getPackage().getName().length();
        return Stream.of(type.getPackage().getName().split("\ \."))
                .map(path -> path.substring(0.1))
                .collect(Collectors.joining(SPLIT_CHAR))
                + SPLIT_CHAR
                + type.getName().substring(packagePathLength + 1); }}Copy the code

The effect is as follows:

Afterword.

With this small optimization, we can reduce unnecessary communication costs on many teams and allow us to focus more on business development.

This article was first published on my blog: monkeywie.cn. Share the knowledge of JAVA, Golang, front-end, Docker, K8S and other dry goods from time to time.