Swagger @ApiModelProperty 绑定枚举类

本文介绍了如何在SpringfoxSwagger中使用自定义注解@ApiModelPropertyEnum绑定枚举值,并通过实现ModelPropertyBuilderPlugin接口,动态生成Swagger模型属性。作者展示了如何处理枚举类和在字段上应用注解。
先看效果

1.自定义注解

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

/**
 * @description:  swagger 属性绑定 枚举对象注解
 * @author guyi
 * @date 2023/11/24 9:53
 **/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiModelPropertyEnum {
    Class value();
}

2.实现Swagger 的 ModelPropertyBuilderPlugin 接口


import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Annotations;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.lang.reflect.*;
import java.util.Map;
import java.util.Optional;

/**
 * @Description: 自定义 Swagger 属性绑定
 * @Author: guyi
 * @Date: 2023-11-24 09:58
 **/
@Component
@Order(-2147482648)
public class MyModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {

    private final DescriptionResolver descriptions;

    @Autowired
    public MyModelPropertyBuilderPlugin(DescriptionResolver descriptions) {
        this.descriptions = descriptions;
    }

    @Override
    public void apply(ModelPropertyContext context) {
        Optional<ApiModelProperty> annotation = Optional.empty();
        if (context.getAnnotatedElement().isPresent()) {
            annotation = (Optional) annotation.map(Optional::of).orElse(ApiModelProperties.findApiModePropertyAnnotation((AnnotatedElement) context.getAnnotatedElement().get()));
        }

        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = (Optional) annotation.map(Optional::of).orElse(Annotations.findPropertyAnnotation((BeanPropertyDefinition) context.getBeanPropertyDefinition().get(), ApiModelProperty.class));
        }

        if (annotation.isPresent()) {
            ApiModelProperty apiModelProperty = annotation.get();
            ApiModelPropertyEnum apiModelPropertyEnum = context.getBeanPropertyDefinition().get().getField().getAnnotation(ApiModelPropertyEnum.class);
            try {
                StringBuilder stringBuilder = null;
                if (apiModelPropertyEnum != null && !apiModelProperty.value().contains("(")) {
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(apiModelProperty);
                    Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
                    memberValues.setAccessible(true);
                    Map o = (Map) memberValues.get(invocationHandler);
                    Class<?> aClass = apiModelPropertyEnum.value();
                    Object[] enumConstants = aClass.getEnumConstants();
                    stringBuilder = new StringBuilder();
                    stringBuilder.append(apiModelProperty.value());
                    stringBuilder.append("(");
                    for (Object enumConstant : enumConstants) {
                        Method[] declaredMethods = aClass.getDeclaredMethods();
                        for (Method declaredMethod : declaredMethods) {
                            String name = declaredMethod.getName();
                            if (name.equals("values") || name.equals("valueOf")) {
                                continue;
                            }
                            stringBuilder.append(declaredMethod.invoke(enumConstant));
                            stringBuilder.append(":");
                        }
                        stringBuilder.delete(stringBuilder.length() - 1, stringBuilder.length());
                        stringBuilder.append(",");
                    }
                    stringBuilder.delete(stringBuilder.length() - 1, stringBuilder.length());
                    stringBuilder.append(")");
                    o.put("value", stringBuilder.toString());
                }
                context.getBuilder()
                        .allowableValues(apiModelProperty.allowableValues() == null ? null : ApiModelProperties.allowableValueFromString(apiModelProperty.allowableValues()))
                        .required(apiModelProperty.required())
                        .readOnly(apiModelProperty.readOnly())
                        .description(stringBuilder == null ? null : stringBuilder.toString())
                        .isHidden(apiModelProperty.hidden())
                        .type(resolve(context.getResolver(), apiModelProperty))
                        .position(apiModelProperty.position());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public ResolvedType resolve(TypeResolver resolver, ApiModelProperty apiModelProperty) {
        try {
            return resolver.resolve(Class.forName(apiModelProperty.dataType()), new Type[0]);
        } catch (ClassNotFoundException e) {
            return resolver.resolve(Object.class, new Type[0]);
        }

    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}

3.定义枚举对象

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * @Description: 通用枚举类,把所有枚举类都写到一起
 * @Author: guyi
 * @Date: 2023-11-24 14:00
 **/
public final class ComEnum {



    /**
     *  通用字典表-是否(0-否,1-是)
     */
    @Getter
    @AllArgsConstructor
    public enum YESORNO {
        NO("0","否"),
        YES("1","是");

        private final String value;
        private final String label;
    }



    /**
     *  荣誉获奖表--获奖级别(1-国家级、2-省级、3-市级、4-区级、5-校级)
     */
    @Getter
    @AllArgsConstructor
    public enum AwardsHistoryLevel {
        NATION("1","国家级"),
//        PROVINCE("2","省级"),
        CITY("3","市级"),
        AREA("4","区级"),
        SCHOOL("5","校级");

        private final String value;
        private final String label;
    }
    

}

4.注解使用

	/**获奖级别*/
    @ApiModelProperty(value = "获奖级别")
    @ApiModelPropertyEnum(value = ComEnum.AwardsHistoryLevel.class)
    private String level;

虽然给定的引用未涉及 Swagger 中 `@ApiModelProperty` 的 `position` 属性排序失败的解决方案,但可以从常见的技术思路来分析解决办法。 ### 检查版本兼容性 确保使用的 Swagger 版本支持 `position` 属性的排序功能。不同版本的 Swagger 对注解属性的支持和实现可能存在差异。如果版本过低,可能不支持该属性或者存在排序功能的 bug。可以尝试升级 Swagger 到最新的稳定版本,例如将 Swagger 2 升级到 Swagger 3(Springdoc-openapi)。 ### 确认属性使用方式 要保证 `position` 属性的值使用正确。`position` 属性是一个整数,值越小的属性在文档中越靠前显示。例如: ```java import io.swagger.annotations.ApiModelProperty; public class ExampleModel { @ApiModelProperty(position = 2, value = "This is the second property") private String secondProperty; @ApiModelProperty(position = 1, value = "This is the first property") private String firstProperty; // Getters and setters public String getSecondProperty() { return secondProperty; } public void setSecondProperty(String secondProperty) { this.secondProperty = secondProperty; } public String getFirstProperty() { return firstProperty; } public void setFirstProperty(String firstProperty) { this.firstProperty = firstProperty; } } ``` ### 检查配置 检查 Swagger 的配置类,确保没有其他配置影响了属性的排序。有时候全局的配置或者自定义的过滤器可能会覆盖 `position` 属性的排序规则。例如,在 Spring Boot 项目中,检查 Swagger 的配置类是否有对属性排序的特殊处理。 ### 缓存问题 有时候,缓存可能会导致排序结果不更新。可以尝试清除项目的缓存,重新启动应用程序,让 Swagger 重新生成文档。 ### 自定义排序规则 如果以上方法都无法解决问题,可以考虑自定义排序规则。通过实现 Swagger 的 `ModelPropertyBuilderPlugin` 接口,自定义属性的排序逻辑。以下是一个简单的示例: ```java import io.swagger.models.properties.Property; import io.swagger.models.properties.StringProperty; import io.swagger.models.properties.IntegerProperty; import io.swagger.annotations.ApiModelProperty; import org.springframework.stereotype.Component; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin; import springfox.documentation.spi.schema.contexts.ModelPropertyContext; import springfox.documentation.swagger.common.SwaggerPluginSupport; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @Component public class CustomApiModelPropertyPositionPlugin implements ModelPropertyBuilderPlugin { @Override public boolean supports(DocumentationType delimiter) { return SwaggerPluginSupport.pluginDoesApply(delimiter); } @Override public void apply(ModelPropertyContext context) { // 自定义排序逻辑 List<Property> properties = context.getBuilder().build().getProperties().values().stream() .sorted(Comparator.comparingInt(p -> { ApiModelProperty apiModelProperty = context.getBeanPropertyDefinition().get().getField().getAnnotation(ApiModelProperty.class); return apiModelProperty != null ? apiModelProperty.position() : 0; })) .collect(Collectors.toList()); // 更新属性顺序 context.getBuilder().properties(properties.stream() .collect(Collectors.toMap(Property::getName, p -> p, (a, b) -> a))); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值