背景
当前公司因为前后端文档交互使用swagger,swagger虽然解决了java文档标识的问题,但是在处理枚举交互的过程中并不能完全展示出key-value的结构,前后端交互带来麻烦,需要手动进行添加处理,这样对开发的成本进行增加。所以自己动手开发swagger的插件来解决前后端交互问题,以及减小开发的成本。
SwaggerDisplayEnum注解定义
定义注解的目的为了标识在枚举参数的列上,这样解析的话,直接可以通过添加注解的方式来进行解析。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 王守钰
* @date 2021-12-27 09:35
* @description swagger展示枚举
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SwaggerDisplayEnum {
/**
* 枚举键,默认值''
* @return 枚举键
*/
String key() default "";
/**
* 枚举值,默认值'value'
* @return 枚举值
*/
String value() default "value";
/**
* 枚举类,必须继承枚举类
* @return 枚举类
*/
Class<? extends Enum> clazz();
}
ModelPropertyBuilderPlugin插件配置
ModelPropertyBuilderPlugin
为实体类参数的解析,通过继承来进行处理参数配置信息,通过判断类的列上是否标记了SwaggerDisplayEnum
注解,通过SwaggerDisplayEnum
进行解析枚举的数据拼接展示。
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.google.common.base.Optional;
import org.springframework.util.ReflectionUtils;
import springfox.documentation.builders.ModelPropertyBuilder;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author 王守钰
* @date 2021-12-27 09:36
* @description swagger枚举模型插件
*/
public class EnumModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
/**
* description列
*/
private static final String DESCRIPTION_FIELD = "description";
/**
* 空字符串
*/
private static final String EMPTY_STR = "";
@Override
public void apply(ModelPropertyContext context) {
Optional<BeanPropertyDefinition> optional = context.getBeanPropertyDefinition();
if (optional.isPresent()) {
try{
AnnotatedField annotatedField = optional.get().getField();
Field field = annotatedField.getAnnotated();
addDescForEnum(context, field);
}catch (Exception e){}
}
}
@Override
public boolean supports(DocumentationType documentationType) {
return true;
}
private void addDescForEnum(ModelPropertyContext context, Field field) {
SwaggerDisplayEnum annotation = field.getAnnotation(SwaggerDisplayEnum.class);
if (annotation != null) {
String key = annotation.key();
String value = annotation.value();
Class<?> enumClass = annotation.clazz();
Object[] enumConstants = enumClass.getEnumConstants();
List<String> displayValues = Arrays.stream(enumConstants)
.filter(Objects::nonNull)
.map(item -> {
Object name = EMPTY_STR;
Class<?> currentClass = item.getClass();
if(null == key || EMPTY_STR.equals(key.trim())){
name = String.valueOf(item);
}else{
Field indexField = ReflectionUtils.findField(currentClass, key);
ReflectionUtils.makeAccessible(indexField);
name = ReflectionUtils.getField(indexField, item);
}
Field descField = ReflectionUtils.findField(currentClass, value);
ReflectionUtils.makeAccessible(descField);
Object desc = ReflectionUtils.getField(descField, item);
return name + ":" + desc;
}).collect(Collectors.toList());
ModelPropertyBuilder builder = context.getBuilder();
Field descField = ReflectionUtils.findField(builder.getClass(), DESCRIPTION_FIELD);
ReflectionUtils.makeAccessible(descField);
String joinText = ReflectionUtils.getField(descField, builder)
+ " (" + String.join("; ", displayValues) + ")";
builder.description(joinText);
}
}
}
启动类配置
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;
/**
* @author 王守钰
* @date 2021-12-27 09:40
* @description swagger插件配置
*/
@Configuration
@ConditionalOnProperty(value = "plugin.swagger.enum-support", matchIfMissing = true)
@ConditionalOnBean(Swagger2DocumentationConfiguration.class)
public class SwaggerEnumPluginConfiguration {
@Bean
public EnumModelPropertyBuilderPlugin enumModelPropertyBuilderPlugin() {
return new EnumModelPropertyBuilderPlugin();
}
}
当配置中Swagger2DocumentationConfiguration
类存在后,配置类自动配置我们的EnumModelPropertyBuilderPlugin
插件,当然也可以通过plugin.swagger.enum-support
参数来进行管控插件的开关。
META-INF配置
additional-spring-configuration-metadata.json配置
{
"properties": [
{
"name": "plugin.swagger.enum-support",
"type": "java.lang.Boolean",
"description": "Enabled Swagger Enum Plugin.",
"defaultValue": true,
"deprecation": {
"reason": "The swagger-enum-plugin auto-configuration is no longer customizable. Provide your own SwaggerPluginConfiguration bean instead.",
"level": "error"
}
}
]
}
spring-configuration-metadata.json配置
{
"properties": [
{
"name": "swagger.enum-support",
"type": "java.lang.Boolean",
"description": "Enabled Swagger Enum Plugin."
}
]
}
spring.factories配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.plugin.swagger.SwaggerEnumPluginConfiguration
注意:com.plugin.swagger
配置为自己项目相关的包信息。
示例效果
实体类
public class AccessRecordMobileDetailVO implements Serializable {
/**
* 拜访类型
*/
@SwaggerDisplayEnum(clazz = AccessVisitTypeEnum.class, key = "type", value = "desc")
@ApiModelProperty(value = "拜访类型", required = true, example = "ONLINE")
private String accessVisitType;
}
枚举类
@Getter
@AllArgsConstructor
public enum AccessVisitTypeEnum {
/**
* 线上
*/
ONLINE("ONLINE","线上"),
/**
* 线下
*/
OFFLINE("OFFLINE","线下"),
;
/**
* 访问类型
*/
private String type;
/**
* 描述
*/
private String desc;
}
效果图
源码地址
https://gitee.com/wxquan/wsy-plugins
Maven仓库坐标
<dependency>
<groupId>com.wangshouyu</groupId>
<artifactId>wsy-plugins</artifactId>
<version>${lastest.version}</version>
</dependency>