一、自定义注解。
一个用于排除,一个用于需要。
-
package com.yx.common.swagger;
-
import java.lang.annotation.ElementType;
-
import java.lang.annotation.Retention;
-
import java.lang.annotation.RetentionPolicy;
-
import java.lang.annotation.Target;
-
/**
-
* 自定义aop注解 支持swagger的动态属性 排除属性
-
* @author cc
-
*/
-
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
-
@Retention(RetentionPolicy.RUNTIME)
-
public @interface ApiIgp {
-
String[] value(); //对象属性值
-
}
-
package com.yx.common.swagger;
-
import java.lang.annotation.ElementType;
-
import java.lang.annotation.Retention;
-
import java.lang.annotation.RetentionPolicy;
-
import java.lang.annotation.Target;
-
/**
-
* 自定义aop注解 支持swagger的动态属性 (只)需要属性
-
* @author cc
-
*/
-
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
-
@Retention(RetentionPolicy.RUNTIME)
-
public @interface ApiNeed {
-
//对象属性值
-
String[] value();
-
}
二、重写 swagger2 的 ParameterBuilderPlugin 支持自定义白名单黑名单注解
注意如下代码要保证生成的名称,不可重复。此处以UUID进行生成。
代码如下
-
package com.yx.user.config;
-
import com.fasterxml.classmate.TypeResolver;
-
import com.google.common.base.Optional;
-
import com.yx.common.swagger.ApiIgp;
-
import com.yx.common.swagger.ApiNeed;
-
import io.swagger.annotations.ApiModelProperty;
-
import lombok.extern.slf4j.Slf4j;
-
import org.apache.commons.lang3.StringUtils;
-
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.StringMemberValue;
-
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.lang.reflect.Field;
-
import java.lang.reflect.Modifier;
-
import java.util.Arrays;
-
import java.util.List;
-
import java.util.Random;
-
import java.util.UUID;
-
import java.util.stream.Collectors;
-
/**
-
* 重写 swagger2 的 ParameterBuilderPlugin 支持自定义白名单黑名单注解
-
* @author xudaz
-
* @date 2021/6/12
-
*/
-
@Component
-
@Order
-
@Slf4j
-
class MyParameterBuilderPlugin implements ParameterBuilderPlugin {
-
@Autowired
-
private TypeResolver typeResolver;
-
@Override
-
public void apply(ParameterContext parameterContext) {
-
ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
-
Class<?> originClass = parameterContext.resolvedMethodParameter().getParameterType().getErasedType();
-
// 排除属性
-
ApiIgp igpOptional = null;
-
@SuppressWarnings("Guava")
-
Optional<ApiIgp> apiIgpOptional = methodParameter.findAnnotation(ApiIgp.class);
-
if ( apiIgpOptional.isPresent() ) {
-
igpOptional = apiIgpOptional.get();
-
}
-
// 需要属性
-
ApiNeed needOptional = null;
-
@SuppressWarnings("Guava")
-
Optional<ApiNeed> apiNeedOptional = methodParameter.findAnnotation(ApiNeed.class);
-
if ( apiNeedOptional.isPresent() ) {
-
needOptional = apiNeedOptional.get();
-
}
-
if (null != igpOptional || null != needOptional ) {
-
Random random = new Random();
-
//model 名称
-
// String name = originClass.getSimpleName() + "YX" + random.nextInt(100000);
-
String name = originClass.getSimpleName() + "_" + UUID.randomUUID().toString().replace("-", "");
-
try {
-
// 排除 (黑名单)
-
if ( null != igpOptional ) {
-
String[] properties = igpOptional.value();
-
parameterContext.getDocumentationContext()
-
.getAdditionalModels()
-
//向documentContext的Models中添加我们新生成的Class
-
.add(typeResolver.resolve(createRefModelIgp(properties, originClass.getPackage()+"."+name, originClass)));
-
}
-
// 需要 (白名单)
-
if ( null != needOptional ) {
-
String[] properties = needOptional.value();
-
parameterContext.getDocumentationContext()
-
.getAdditionalModels()
-
//向documentContext的Models中添加我们新生成的Class
-
.add(typeResolver.resolve(createRefModelNeed(properties, originClass.getPackage()+"."+name, originClass)));
-
}
-
} catch (Exception e) {
-
log.error("swagger切面异常", e);
-
}
-
//修改Map参数的ModelRef为我们动态生成的class
-
parameterContext.parameterBuilder()
-
.parameterType("body")
-
.modelRef(new ModelRef(name))
-
.name(name);
-
}
-
}
-
/**
-
* 创建自定义mode给swagger2 排除参数
-
* @param properties 需要排除的参数
-
* @param name model 名称
-
* @param origin originClass
-
* @return r
-
*/
-
private Class<?> createRefModelIgp(String[] properties, String name, Class<?> origin) {
-
ClassPool pool = ClassPool.getDefault();
-
// 动态创建一个class
-
CtClass ctClass = pool.makeClass( name);
-
try {
-
Field[] fields = origin.getDeclaredFields();
-
List<Field> fieldList = Arrays.asList(fields);
-
List<String> ignoreProperties = Arrays.asList(properties);
-
// 过滤掉 properties 的参数
-
List<Field> dealFields = fieldList.stream().filter(s -> !ignoreProperties.contains(s.getName())).collect(Collectors.toList());
-
addField2CtClass(dealFields, origin, ctClass);
-
return ctClass.toClass();
-
} catch (Exception e) {
-
log.error("swagger切面异常", e);
-
return null;
-
}
-
}
-
/**
-
* 创建自定义mode给swagger2 需要参数
-
* @param properties 需要排除的参数
-
* @param name model 名称
-
* @param origin originClass
-
* @return r
-
*/
-
private Class<?> createRefModelNeed(String[] properties, String name, Class<?> origin) {
-
ClassPool pool = ClassPool.getDefault();
-
CtClass ctClass = pool.makeClass( name);
-
try {
-
Field[] fields = origin.getDeclaredFields();
-
List<Field> fieldList = Arrays.asList(fields);
-
List<String> ignoreProperties = Arrays.asList(properties);
-
// 过滤掉 非 properties 的参数
-
List<Field> dealFields = fieldList.stream().filter(s -> ignoreProperties.contains(s.getName())).collect(Collectors.toList());
-
addField2CtClass(dealFields, origin, ctClass);
-
return ctClass.toClass();
-
} catch (Exception e) {
-
log.error("swagger切面异常", e);
-
return null;
-
}
-
}
-
private void addField2CtClass(List<Field> dealFields, Class<?> origin, CtClass ctClass ) throws NoSuchFieldException, NotFoundException, CannotCompileException {
-
// 倒序遍历
-
for (int i = dealFields.size() - 1; i >= 0; i--) {
-
Field field = dealFields.get(i);
-
CtField ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()), field.getName(), ctClass);
-
ctField.setModifiers(Modifier.PUBLIC);
-
ApiModelProperty ampAnno = origin.getDeclaredField(field.getName()).getAnnotation(ApiModelProperty.class);
-
String attributes = java.util.Optional.ofNullable(ampAnno).map(ApiModelProperty::value).orElse("");
-
//添加model属性说明
-
if (StringUtils.isNotBlank(attributes) ){
-
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(attributes, constPool));
-
attr.addAnnotation(ann);
-
ctField.getFieldInfo().addAttribute(attr);
-
}
-
ctClass.addField(ctField);
-
}
-
}
-
@Override
-
public boolean supports(DocumentationType documentationType) {
-
return true;
-
}
-
}
三、使用实例