swagger

博客围绕Java展开,介绍了常用注解,重点阐述自定义注解(POST),提及黑名单和白名单模式,还说明了实现Swagger提供的插件以支持这两种自定义注解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常用注解
//类上
@Api(tags = "配件信息")

//方法上
@ApiOperation("新增配件信息")

//方法上入参
@ApiImplicitParams({
    @ApiImplicitParam(name = "rootid", value = "物料id"),
    @ApiImplicitParam(name = "lid", value = "树节点id"),
    @ApiImplicitParam(name = "soutype", value = "搜索类型"),
})

//方法上 返回对象
@ApiResponses({
    @ApiResponse(code = 200,message = "OK",response = TrainUserTEntity.class),
})


//实体类属性/方法上
@ApiModelProperty(value = "关联产品ID",required = true)

//方法的形参上
@ApiParam(value = "合同编号", required = true)
自定义注解(POST)
黑名单模式

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义aop注解 支持swagger的动态属性 排除属性
 * @author xudaz
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIgp {
    String[] value(); //对象属性值
}

白名单模式
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义aop注解 支持swagger的动态属性 (只)需要属性
 * @author xudaz
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiNeed {
    //对象属性值
    String[] value();
}

实现swagger提供的插件 支持上面俩自定义注解
package com.jeesite.modules.common.swagger;


import com.fasterxml.classmate.TypeResolver;
import io.swagger.annotations.ApiModelProperty;
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.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author mxh
 */
@Order
@Component
public class SwaggerModelReader implements ParameterBuilderPlugin {

    private TypeResolver typeResolver;

    private static final Logger logger = LoggerFactory.getLogger(SwaggerModelReader.class);

    @Autowired
    public void setTypeResolver(TypeResolver typeResolver) {
        this.typeResolver = typeResolver;
    }

    @Override
    public void apply(ParameterContext parameterContext) {
        ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
        Optional<ApiIgp> ignorePropsOptional = methodParameter.findAnnotation(ApiIgp.class);
        Optional<ApiNeed> needPropsOptional = methodParameter.findAnnotation(ApiNeed.class);
        String apiUrl = parameterContext.getOperationContext().requestMappingPattern();
        if (ignorePropsOptional.isPresent() && needPropsOptional.isPresent()) {
            // 2个注解不能同时存在于一个参数上
            StringBuilder errorMsg = new StringBuilder("@DaisyApiIgnoreProps with @DaisyApiNeedProps cannot exist at the same time! ");
            errorMsg.append("api路径:").append(apiUrl).append("\t");
            Optional<String> defaultName = parameterContext.resolvedMethodParameter().defaultName();
            if (defaultName.isPresent()) {
                errorMsg.append("参数名:").append(defaultName.get()).append("\t");
            }
            // 写一个错误日志 但是不影响启动 因为这个插件并不会影响到系统的正常运行
            logger.error(errorMsg.toString());
        }
        if (ignorePropsOptional.isPresent() || needPropsOptional.isPresent()) {
            // 如果是泛型的话 那么获取泛型里面的数据
            Class originClass = parameterContext.resolvedMethodParameter().getParameterType().getErasedType();
            boolean isCollection = Collection.class.isAssignableFrom(originClass);
            // 如果参数是集合类型的话 那么原始类应该
            if (isCollection) {
                // 获取泛型类型
                originClass = parameterContext.resolvedMethodParameter().getParameterType().getTypeBindings().getBoundType(0).getErasedType();
            }
            //model 名称
			//String name = originClass.getSimpleName() + apiUrl.replaceAll("/", "_");
            Random random = new Random();
            String name = originClass.getSimpleName() + random.nextInt(100);;
            String[] properties;
            boolean ignore = ignorePropsOptional.isPresent();
            if (ignore) {
                properties = ignorePropsOptional.get().value();
            } else {
                properties = needPropsOptional.get().value();
            }
            // 向文档内容中添加新的模型对象
            parameterContext.getDocumentationContext()
                    .getAdditionalModels()
                    //像documentContext的Models中添加我们新生成的Class
                    .add(typeResolver.resolve(createRefModelIgp(properties, name, originClass, ignore)));
            ModelRef modelRef;
            if (!isCollection) {
                modelRef = new ModelRef(name);
            } else {
                modelRef = new ModelRef("List", new ModelRef(name));
            }
            //修改model参数的ModelRef为我们动态生成的class
            parameterContext.parameterBuilder()
                    .parameterType("body")
                    .modelRef(modelRef)
                    .name(name);
        }

    }

    /**
     * 根据propertys中的值动态生成含有Swagger注解的javaBeen
     */
    private Class createRefModelIgp(String[] propertys, String name, Class origin, boolean ignore) {
        ClassPool pool = ClassPool.getDefault();
        String newClassName = origin.getPackage().getName() + "." + name;
        try {
            CtClass ctClass = pool.getOrNull(newClassName);
            if (ctClass != null) {
                // 这行代码很重要 如果没有的话 那就不能和devTool 一起使用
                // 销毁旧的类
                ctClass.detach();
            }
            // 通过javassit 技术创建一个类
            ctClass = pool.makeClass(origin.getPackage().getName() + "." + name);
            // 获取所有的属性
            List<Field> fieldList = getAllField(origin, null);
            List<String> dealProperties = Arrays.asList(propertys);
            List<Field> dealFileds = fieldList.stream().filter(
                    s -> ignore ? (!(dealProperties.contains(s.getName()))) : dealProperties.contains(s.getName())
            ).collect(Collectors.toList());
            // 创建属性
            createCtFileds(dealFileds, ctClass);
            return ctClass.toClass();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取一个类中的所以属性
     *
     * @param clazz
     * @param fieldList
     * @return
     */
    private static List<Field> getAllField(Class clazz, List<Field> fieldList) {
        if (fieldList == null) {
            fieldList = new ArrayList<>(16);
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        if (declaredFields.length > 0) {
            fieldList.addAll(Arrays.asList(declaredFields));
        }
        // 获取父类属性
        Class superclass = clazz.getSuperclass();
        if (!superclass.equals(Object.class)) {
            // 重复处理父类
            fieldList = getAllField(superclass, fieldList);
        }
        return fieldList;
    }

    /**
     * 通过字节码重新创建一些属性
     *
     * @param dealFileds
     * @param ctClass
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    public void createCtFileds(List<Field> dealFileds, CtClass ctClass) throws CannotCompileException, NotFoundException {
        // 获取常量池
        ConstPool constPool = ctClass.getClassFile().getConstPool();
        for (Field field : dealFileds) {
            CtField ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()), field.getName(), ctClass);
            ctField.setModifiers(Modifier.PUBLIC);
            //添加model属性说明
            AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
            Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool);
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            if (apiModelProperty != null) {
                String[] valNames = new String[]{"name", "value", "example", "allowableValues", "access", "notes", "dataType", "reference"};
                String[] vals = new String[]{apiModelProperty.name(),
                        apiModelProperty.value(), apiModelProperty.example(),
                        apiModelProperty.allowableValues(), apiModelProperty.access(),
                        apiModelProperty.notes(), apiModelProperty.dataType(), apiModelProperty.reference()};

                for (int i = 0; i < valNames.length; i++) {
                    if (!"".equals(vals[i])) {
                        ann.addMemberValue(valNames[i], new StringMemberValue(vals[i], constPool));
                    }
                }

                // 处理hidden
                ann.addMemberValue("hidden", new BooleanMemberValue(apiModelProperty.hidden(), constPool));
                // 处理required
                ann.addMemberValue("required", new BooleanMemberValue(apiModelProperty.required(), constPool));
            }

            attr.addAnnotation(ann);
            ctField.getFieldInfo().addAttribute(attr);
            ctClass.addField(ctField);
        }
    }


    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值