brief history
Let's take a look at the relationship between springfox and swagger.
swagger is a popular API development framework. Based on the "open API specification" (OAS), this framework provides corresponding solutions for the whole API development cycle. It is a very huge project (including design, coding and testing, and supports almost all languages).
OAS itself is an API specification, which is used to describe a complete set of API interfaces, including whether an interface is a GET or POST request, and which parameters and header s are included in this file. It is usually designed in YAML format, which is more convenient to write, and most of it will be in the form of json when transmitted in the network, because json has strong universality.
Due to the popularity of Spring, Marty Pitt wrote a Spring based component, swagger springmvc, to integrate swagger into springmvc. Springfox is developed from this component. At the same time, springfox is also a new project. This paper still uses one of the components, springfox-swagger2.
pringfox-swagger2 still relies on OSA specification documents, that is, a json file describing API, and the function of this component is to help us automatically generate this json file. Another component we will use, springfox swagger UI, parses this json file and presents it in a more friendly way.
SpringFox
Github: https://github.com/springfox/springfox
Automated JSON API documentation for API's built with Spring.
Getting Started
For new projects
For Maven
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency>
For Gradle
implementation "io.springfox:springfox-boot-starter:<version>"
Scalable components of Swagger
In the source code( https://github.com/springfox/springfox ), you can see some interface files at the end of Plugin as shown in the figure below. We are going to work on these.

To customize the extension function, you only need to implement the apply method in an xxxPlugin interface. In the apply method, we manually scan our custom annotations, and then add the logic of the relevant implementation.
Code example:
/** * User defined annotation for parameter passing value * @author zhenghui * @date 2020 September 13, 2013 13:25:18 * @desc Read the custom properties and generate the model dynamically */ @Configuration @Order(-19999) //plugin loading order: the default is the last loading public class SwaggerModelReader implements ParameterBuilderPlugin { @Autowired private TypeResolver typeResolver; static final Map<String,String> MAPS = new HashMap<>(); static { MAPS.put("byte","java.lang.Byte"); MAPS.put("short","java.lang.Short"); MAPS.put("integer","java.lang.Integer"); MAPS.put("long","java.lang.Long"); MAPS.put("float","java.lang.Float"); MAPS.put("double","java.lang.Double"); MAPS.put("char","java.lang.Character"); MAPS.put("string","java.lang.String"); MAPS.put("boolean","java.lang.Boolean"); } //Get the class location of the package where the type is located according to the user-defined type static public String getTypePath(String key){ return key==null || !MAPS.containsKey(key.toLowerCase()) ? null : MAPS.get(key.toLowerCase()); } @Override public void apply(ParameterContext context) { ResolvedMethodParameter methodParameter = context.resolvedMethodParameter(); //Custom annotations Optional<ApiIgp> apiIgp = methodParameter.findAnnotation(ApiIgp.class); Optional<Apicp> apicp = methodParameter.findAnnotation(Apicp.class); if (apiIgp.isPresent() || apicp.isPresent()) { Class originClass = null; String[] properties = null; //Parameters passed by annotation Integer annoType = 0;//Type of annotation String name = null + "Model" + 1; //model name / / parameter name String[] noValues = null; String[] noValueTypes = null; String[] noVlaueExplains = null; //Get the parameters passed by the custom annotation if (apiIgp.isPresent()){ properties = apiIgp.get().values(); //Excluded originClass = apiIgp.get().classPath();//class of the original object name = apiIgp.get().modelName() ; //model name / / parameter name noValues = apiIgp.get().noValues(); noValueTypes = apiIgp.get().noValueTypes(); noVlaueExplains = apiIgp.get().noVlaueExplains(); }else { properties = apicp.get().values(); //Needed annoType = 1; originClass = apicp.get().classPath();//class of the original object name = apicp.get().modelName() ;//Name of custom class noValues = apicp.get().noValues(); noValueTypes = apicp.get().noValueTypes(); noVlaueExplains = apicp.get().noVlaueExplains(); } //Generate a new class Class newClass = createRefModelIgp(properties, noValues, noValueTypes, noVlaueExplains, name, originClass, annoType); context.getDocumentationContext() .getAdditionalModels() .add(typeResolver.resolve(newClass)); //Add our newly generated Class to the Models of documentContext context.parameterBuilder() //Modify the ModelRef of the model parameter to dynamically generate the class for us .parameterType("body") .modelRef(new ModelRef(name)) .name(name); } } /** * * @param properties annoType=1:Expected annoType=0: excluded * @param noValues * @param noValueTypes * @param noVlaueExplains * @param name The name of the mode created * @param origin * @param annoType Type of annotation * @return */ private Class createRefModelIgp(String[] properties, String[] noValues, String[] noValueTypes, String[] noVlaueExplains, String name, Class origin, Integer annoType) { try { //Gets all variables in the original entity class Field[] fields = origin.getDeclaredFields(); //Convert to a List collection to facilitate stream filtering List<Field> fieldList = Arrays.asList(fields); //The passed in parameters are also converted to List List<String> dealProperties = Arrays.asList(properties);//Remove spaces and separate with commas //Filter out existing List<Field> dealFileds = fieldList .stream() .filter(s -> annoType==0 ? (!(dealProperties.contains(s.getName()))) //If the annotation type is 0, it indicates that it should be reversed : dealProperties.contains(s.getName()) ).collect(Collectors.toList()); //Store variables that do not exist List<String> noDealFileds = Arrays.asList(noValues); List<String> noDealFiledTypes = Arrays.asList(noValueTypes); List<String> noDealFiledExplains = Arrays.asList(noVlaueExplains); //Create a class ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass(origin.getPackage().getName()+"."+name); //Create the object and add the existing variables createCtFileds(dealFileds,noDealFileds,noDealFiledTypes,noDealFiledExplains,ctClass,annoType); //Returns the final class return ctClass.toClass(); } catch (Exception e) { e.printStackTrace(); return null; } } @Override public boolean supports(DocumentationType delimiter) { return true; } /** * Dynamically generate JavaBean with Swagger annotation according to the value in properties * * @param dealFileds The name of an object property that already exists in the original object * @param noDealFileds Object property name that does not exist in the original object * @param noDealFiledTypes The types of object attributes that do not exist in the original object, including eight basic types, such as dounle and String * @param noDealFiledExplains Parameter description of custom variable * @param ctClass Source class * @throws CannotCompileException * @throws NotFoundException * @throws ClassNotFoundException */ public void createCtFileds(List<Field> dealFileds, List<String> noDealFileds, List<String> noDealFiledTypes,List<String> noDealFiledExplains, CtClass ctClass, Integer annoType) { //Add variables existing in the original entity class // if(annoType==1) for (Field field : dealFileds) { CtField ctField = null; try { ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()), field.getName(), ctClass); } catch (CannotCompileException e) { System.out.println("1 not found:"+e.getMessage()); } catch (NotFoundException e) { System.out.println("2 not found:"+e.getMessage()); } ctField.setModifiers(Modifier.PUBLIC); ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class); String apiModelPropertyValue = java.util.Optional.ofNullable(annotation).map(s -> s.value()).orElse(""); if (StringUtils.isNotBlank(apiModelPropertyValue)) { //Add model attribute description ConstPool constPool = ctClass.getClassFile().getConstPool(); AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool); ann.addMemberValue("value", new StringMemberValue(apiModelPropertyValue,constPool)); attr.addAnnotation(ann); ctField.getFieldInfo().addAttribute(attr); } try { ctClass.addField(ctField); } catch (CannotCompileException e) { System.out.println("Cannot add field 1:"+e.getMessage()); } } //Add a variable that does not exist in the original entity class for (int i = 0; i < noDealFileds.size(); i++) { String valueName = noDealFileds.get(i);//Variable name String valueType = noDealFiledTypes.get(i);//Type of variable valueType=getTypePath(valueType); //Depending on the type of variable, the name of the variable, and the class in which the variable will be created CtField ctField = null; try { ctField = new CtField(ClassPool.getDefault().get(valueType), valueName, ctClass); } catch (CannotCompileException e) { System.out.println("3 not found:"+e.getMessage()); } catch (NotFoundException e) { System.out.println("Could not find 4:"+e.getMessage()); } ctField.setModifiers(Modifier.PUBLIC);//Set whether the permission range is private or public if(noDealFiledExplains.size()!=0){ //Parameter setting description String apiModelPropertyValue = (apiModelPropertyValue=noDealFiledExplains.get(i))==null?"No description":apiModelPropertyValue;//Parameter description System.out.println(apiModelPropertyValue); if (StringUtils.isNotBlank(apiModelPropertyValue)) { //Add model attribute description ConstPool constPool = ctClass.getClassFile().getConstPool(); AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool); ann.addMemberValue("value", new StringMemberValue(apiModelPropertyValue,constPool)); attr.addAnnotation(ann); ctField.getFieldInfo().addAttribute(attr); } } //Add this variable to the class try { ctClass.addField(ctField); } catch (CannotCompileException e) { System.out.println("Cannot add field 2:"+e.getMessage()); } } } }
Swagger common notes
@Api
Used on a class to illustrate the function of the class
@Api(value = "UserController", description = "User related api")
@ApiOperation
Used in the method to explain the function of the method
@ApiOperation(value = "Find users", notes = "Find users", httpMethod = "GET", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ApiImplicitParams
Used to include a set of parameter descriptions on a method
@ApiImplicitParam
Used in the @ ApiImplicitParams annotation to specify various aspects of a request parameter paramType: where is the parameter placed
header–>Acquisition of request parameters:@RequestHeader query–>Acquisition of request parameters:@RequestParam path(be used for restful Interface)–>Acquisition of request parameters:@PathVariable body((not commonly used) form((not commonly used) name: Parameter name dataType: Parameter type required: Whether the parameter must be passed value: Meaning of parameters defaultValue: Default values for parameters
@ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "only id", required = true, dataType = "Long", paramType = "path"), })
@ApiResponses
Used to represent a set of responses
@ApiResponse
Used in @ ApiResponses, it is generally used to express an error response message code: number, e.g. 400 message: information, such as "request parameters are not filled in" response: the class that threw the exception
@ApiResponses(value = { @ApiResponse(code = 400, message = "No Name Provided") })
@ApiModel
Swagger-core builds the model definitions based on the references to them throughout the API introspection.
The @ApiModel allows you to manipulate the meta data of a model from a simple description or name change to a definition of polymorphism.
Describe the information of a Model (this is generally used in the scenario of using @ RequestBody when creating a post, and the request parameters cannot be described with @ ApiImplicitParam annotation)
@ApiModel(value = "User entity class")
@ApiModelProperty
Describes the properties of a model
@ApiModelProperty(value = "log on user") @ApiIgnore //Use this annotation to ignore this interface
reference material
https://blog.csdn.net/qq_17623363/article/details/109259315 https://blog.csdn.net/wsh900221/article/details/80508548