SpringBoot下枚举治理方案

枚举治理

背景

1、多版本运行开发枚举容易导致冲突,服务无感知

2、业务中使用枚举实例处理逻辑,API入参/返参的枚举类型,需要手动转换

3、API文档(Swagger)枚举描述需要手动列举枚举值,枚举添加/修改描述无法及时更新

4、数据库中的枚举字段,查询出来后需要根据枚举处理,得先转化实例判断,冗余复杂

5、API返回枚举展示值需要手动设置,重复代码较多

6、API接受枚举类型参数的校验场景,冗余复杂

解决方案

1、系统启动检查枚举是否存在冲突

2、API入参/返参支持序列化枚举类型

3、Dao持久层框架(mybatis),出入参支持枚举类型

4、swagger文档定制枚举动态渲染

5、API返回枚举展示值需要手动设置,重复代码较多

6、基于Validation支持枚举类型校验

实现方案

系统启动检查枚举是否存在冲突

  1. 定义枚举注解,标识组件管理的枚举
  2. 实现BeanDefinitionRegistryPostProcessor,启动扫描所有枚举类
  3. 遍历枚举值,检测是否存在冲突

image-20211105141116817

API入参/返参支持序列化枚举类型(系统统一使用fastjson)

  1. Fastjson调研:
    默认枚举序列化器:com.alibaba.fastjson.serializer.EnumSerializer
    默认枚举反序列化器:com.alibaba.fastjson.parser.deserializer.EnumDeserializer

  2. 自定义重写EnumSerializer、EnumDeserializer 序列化、反序列化器

  3. 覆盖fastjson FastJsonHttpMessageConverter默认配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Component
    public class FastJsonPostProcessor implements BeanPostProcessor{

    @Override
    public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException{
    if(bean instanceof FastJsonHttpMessageConverter){
    FastJsonHttpMessageConverter messageConverter = (FastJsonHttpMessageConverter)bean;
    FastJsonConfig fastJsonConfig = messageConverter.getFastJsonConfig();
    fastJsonConfig.setParserConfig(new EnumParserConfig());
    fastJsonConfig.setSerializeConfig(new EnumSerializeConfig());
    }
    return bean;
    }

    }

Dao持久层框架(mybatis)出入参支持枚举类型

  1. 自定义枚举类型转换器TypeHandler
  2. 注入所有枚举类型转换器到mybatis的SqlSessionFactory

Swagger文档定制枚举动态渲染

  1. 重写ModelPropertyBuilderPlugin自定义枚举,动态列举枚举实例描述

API返回枚举展示值自动渲染输出

  1. 举类型字段abc序列化时,在同级目录自动输出abcStr展示字段 如:

    image-20211105110343791

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    @EnumAnnotation
    public enum ServerType {
    /**
    * 1 - ISC
    */
    ISC(1,"ISC"),
    /**
    * 2 - 萤石
    */
    YS(2,"萤石"),
    ;
    @Getter
    @EnumValue
    private final Integer code;
    @Getter
    @EnumName
    private final String name;

    ServerType(Integer code, String name) {
    this.code = code;
    this.name = name;
    }

    public static class DefaultEnumMatcher implements EnumMatcher<VideoType>{

    @Override
    public boolean matches(VideoType targetEnum){
    return Arrays.asList(VideoType.values()).contains(targetEnum);
    }
    }
    }
    在序列化出参时,转化为
    {
    "serverType":1,
    "serverTypeStr":"ISC"
    }

基于Validation支持枚举类型校验

  1. 自定义@EnumValid注解,用于标注需要校验的枚举字段
  2. 自定义ConstraintValidator,校验@EnumValid
  3. 定义枚举时,定义默认枚举校验器如上文:ServerType.DefaultEnumMatcher

解决痛点

  1. API入参支持使用枚举类型接受
  2. API入参枚举类型支持校验
  3. API出参自动渲染枚举展示名称
  4. 持久层出入参支持枚举类型
  5. API枚举入参支持swagger动态渲染
  6. 启动自动监测枚举冲突

缺点

1、修改了实体的序列化反序列化方式,不支持序列化后反序列化或者反序列化后序列化操作

2、依赖FastJson、Swagger3.0、Mybatis,版本兼容性较低、普适性较低

3、API出参序列化输出枚举展示字段,但是暂不支持在Swagger上输出该字段

后续优化

1、API出参序列化输出枚举展示字段,支持在Swagger上输出该字段