在swagger上生成关于json数据类型返回结果的描述
实现原理:在MyOperationBuilderPlugin类中读取controller类方法上的ApiReturnJsonArray注解和ApiReturnJsonObject注解,动态构建对应数据结构的类。
- ApiReturnJsonArray表示的是list数据结构(用于接口返回值类型声明)
- ApiReturnJsonObject表示map数据结构(用于接口返回值类型声明)
- ApiJsonProperty表示key-value数据结构
- ApiJsonObject表示map数据结构(用于接口入参声明)
注:目前代码只实现了List<Map<String,Object>>和Map<String,Object>两种结构,如果json数据结构存在三四层及以上,则需另外定义新的注解。总的来说,差不多是一层json数据结构就要定义一个注解,因为注解不允许继承和自引用。具体实现方式参照ApiReturnJsonArray、ApiReturnJsonObject、ApiJsonProperty的关系。
swagger的代码示例,可点击此处进行下载,或在浏览器地址栏输入https://download.csdn.net/download/qq_31601531/12504190
注解ApiReturnJsonObject表示返回结果的map结构
package com.hua.demo.swagger.dto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 返回结果描述注解
* @author hua
* */
@Target({
ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiReturnJsonObject {
ApiJsonProperty[] value(); //对象属性值
String name() default "ReturnMap"; //类名
}
注解ApiReturnJsonArray表示返回结果的数组结构
package com.hua.demo.swagger.dto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 返回结果描述注解-数组
* @author hua
* */
@Target({
ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiReturnJsonArray {
ApiReturnJsonObject[] values();
String name() default "ReturnList"; //类名
}
注解ApiJsonProperty表示map中一个key-value的数据
package com.hua.demo.swagger.dto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 接口文档入参字段说明
* @author hua
* */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonProperty {
/**
* 参数名称
* */
String name();
/**
* 参数的中文含义
* */
String value() default "";
/**
* 参数示例
* */
String example() default "";
/**
* 支持各种基础数据类型、或包装的数据类型
* */
String dataType() default "string";
/**
* 参数描述(想要在参数旁有中文解析,要给此属性赋值)
* */
String description() default "";
/**
* 是否必填
* */
boolean required() default false;
//支持string 和 int
Class type() default String.class;
}
注解ApiJsonObject表示接口入参的map结构
package com.hua.demo.swagger.dto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 接口文档入参对象说明
* @author hua
* */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonObject {
ApiJsonProperty[] value(); //对象属性值
String name() default "ParamMap"; //对象名
}
MyBaseBuildPlugin类用于动态构造一个类代表注解ApiReturnJsonArray和ApiReturnJsonObject的数据结果
注:类名不能重复,不然会报frozon class的错
package com.hua.demo.swagger.plugin;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.hua.demo.swagger.dto.ApiJsonProperty;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.*;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author hua
* */
@Component
public class MyBaseBuildPlugin {
@Autowired(required = false)
protected TypeResolver typeResolver;
/**
* 参数的全局索引,用于动态生成类,确保类名不会重复
* */
public static int paramIndex = 0;
private ClassPool pool = ClassPool.getDefault();
/**
* 用于标记json结构中描述数据结构的key值
* */
private String childKey = "child";
/**
* 动态生成的Class名
* */
protected final static String basePackage = "com.hua.demo.swagger.dto.";
protected final String StringTypeName = "java.lang.String";
protected final String IntegerTypeName = "java.lang.Integer";
protected final String LongTypeName = "java.lang.Long";
protected final String FloatTypeName = "java.lang.Float";
protected final String DoubleTypeName = "java.lang.Double";
protected final String BooleanTypeName = "java.lang.Boolean";
protected final String ShortTypeName = "java.lang.Short";
/**
* 根据propertys中的值动态生成含有Swagger注解的javaBeen
* @param context
* @param propertys
* @param name 类名
*/
protected Class createRefModel(DocumentationContext context, ApiJsonProperty[] propertys, String name) {
CtClass ctClass = pool.makeClass(basePackage + name);
try {
this.translatePropertyToJSONObject(context,ctClass,propertys);
Class clazz = ctClass.toClass();
ResolvedType resolvedType = typeResolver.resolve(clazz);
context.getAdditionalModels().add(resolvedType);
return clazz;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/***
* 此方法是实现了把当前方法上的所有ApiJsonProperty注解进行特定解析,解析成对应结构的json
* 1、首先根据是否含有'.'来进行分组
* 2、把含有'.'的部分重新进行解析,并把解析后的结果放到不含'.'的结果之中
* 3、把解析完成的数据进行
* @param context
* @param ctClass
* @param propertys
* */
protected void translatePropertyToJSONObject(DocumentationContext context,CtClass ctClass,ApiJsonProperty[] propertys) throws NotFoundException, CannotCompileException{
List<ApiJsonProperty> propertyList = new ArrayList<ApiJsonProperty>();
JSONObject propertyMap = new JSONObject();
for(ApiJsonProperty property : propertys){
if(property.name().contains(".")){
propertyList.add(property);
}else{
JSONObject propertyObj = this.transferAnnationToJSONObject(property);
propertyMap.put(property.name(),propertyObj);
}
}
for(ApiJsonProperty property : propertyList){
JSONObject propertyObj = this.transferAnnationToJSONObject(property);
String name = property.name();
String[] names = name.split("\\.");
propertyObj.put("name",names[1]);
if(property.name().contains("[") && property.name().contains("]")){
String paramName = names[0].substring(0,names[0].indexOf("["));