package com.qmth.distributed.print.config; import com.fasterxml.classmate.TypeResolver; import com.qmth.teachcloud.common.annotation.ApiJsonObject; import com.qmth.teachcloud.common.annotation.ApiJsonProperty; import com.qmth.teachcloud.common.contant.SystemConstant; import org.apache.ibatis.javassist.*; import org.apache.ibatis.javassist.bytecode.AnnotationsAttribute; import org.apache.ibatis.javassist.bytecode.ConstPool; import org.apache.ibatis.javassist.bytecode.annotation.Annotation; import org.apache.ibatis.javassist.bytecode.annotation.BooleanMemberValue; import org.apache.ibatis.javassist.bytecode.annotation.StringMemberValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import springfox.documentation.schema.ModelRef; import springfox.documentation.service.ResolvedMethodParameter; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.ParameterBuilderPlugin; import springfox.documentation.spi.service.contexts.ParameterContext; import java.util.Map; import java.util.Optional; /** * @Description: swagger2 map参数说明插件 * @Param: * @return: * @Author: wangliang * @Date: 2020/4/23 */ @Component @Order //plugin加载顺序,默认是最后加载 public class MapApiReader implements ParameterBuilderPlugin { private final static Logger log = LoggerFactory.getLogger(MapApiReader.class); @Autowired private TypeResolver typeResolver; @Override public void apply(ParameterContext parameterContext) { ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter(); if (methodParameter.getParameterType().canCreateSubtype(Map.class) || methodParameter.getParameterType().canCreateSubtype(String.class)) { //判断是否需要修改对象ModelRef,这里我判断的是Map类型和String类型需要重新修改ModelRef对象 Optional optional = methodParameter.findAnnotation(ApiJsonObject.class); //根据参数上的ApiJsonObject注解中的参数动态生成Class if (optional.isPresent()) { String name = optional.get().name(); //model 名称 ApiJsonProperty[] properties = optional.get().value(); parameterContext.getDocumentationContext().getAdditionalModels().add(typeResolver.resolve(createRefModel(properties, name))); //像documentContext的Models中添加我们新生成的Class parameterContext.parameterBuilder() //修改Map参数的ModelRef为我们动态生成的class .parameterType("body") .modelRef(new ModelRef(name)) .name(name); } } } private final static String basePackage = "com.qmth.teachcloud.entity."; //动态生成的Class名 /** * 根据propertys中的值动态生成含有Swagger注解的javaBeen */ private Class createRefModel(ApiJsonProperty[] propertys, String name) { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass(basePackage + name); try { for (ApiJsonProperty property : propertys) { ctClass.addField(createField(property, ctClass)); } return ctClass.toClass(); } catch (Exception e) { log.error(SystemConstant.LOG_ERROR, e); return null; } } /** * 根据property的值生成含有swagger apiModelProperty注解的属性 */ private CtField createField(ApiJsonProperty property, CtClass ctClass) throws NotFoundException, CannotCompileException { CtField ctField = new CtField(getFieldType(property.type()), property.key(), ctClass); ctField.setModifiers(Modifier.PUBLIC); ConstPool constPool = ctClass.getClassFile().getConstPool(); AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag); Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool); ann.addMemberValue("value", new StringMemberValue(property.description(), constPool)); ann.addMemberValue("required", new BooleanMemberValue(property.required(), constPool)); attr.addAnnotation(ann); ctField.getFieldInfo().addAttribute(attr); return ctField; } private CtClass getFieldType(String type) throws NotFoundException { CtClass fileType = null; switch (type) { case "string": fileType = ClassPool.getDefault().get(String.class.getName()); break; case "int": fileType = ClassPool.getDefault().get(Integer.class.getName()); break; case "long": fileType = ClassPool.getDefault().get(Long.class.getName()); break; case "boolean": fileType = ClassPool.getDefault().get(Boolean.class.getName()); break; default: break; } return fileType; } @Override public boolean supports(DocumentationType delimiter) { return true; } }