通过注解一次解决枚举类在spring中的3种序列化的方式

本文介绍了一个巧妙的解决方案,通过自定义注解和接口,实现枚举类在MyBatis、JSON序列化和SpringMVC controller中自动转换。该方案允许在存储时使用数字ID,传输时使用字符串,同时提供了定制化的序列化和反序列化方式。通过注解配置,可以在Spring Boot项目中轻松应用,避免手动转换的繁琐。示例展示了如何在数据库存储、JSON序列化和HTTP请求参数中使用此功能。

在日常的项目中,往往有很多枚举状态出现,比如性别、账户类型、订单状态等等,在代码编写阶段,对于这种状态类型,使用枚举类是非常方便的,但是由于为了方便与前端或数据库的网络传输、映射和存储,往往会采用约定数字或者特定字符来标识状态,我们有需要将枚举读写为数字或字符,如果用枚举,就要到处添加转换,如果不用枚举,就要头疼会不会写错状态。

怎么解决这个困扰呢?我们先来看最终效果:

只需一个注解,即可配置好让枚举在mybatis存储到数据库时采用数字id,json序列化传给前端时采用字符串,在controller的RequestParam 采用id反序列化为枚举 如果需要也可以通过实现指定接口来详细设定序列化的值

最重要的是一切对于springboot项目都是自动化的,完全可以作为基础组件来在各个项目中引入即可开箱即用

众所周知,在spring中,常见的序列化有3种,分别是 mybatis、json、request

  • mybatis 序列化
    • 通过TypeHandler完成java类型和数据库类型互相转换

  • json序列化(以spring默认json组件 jackson为例)
    • 通过JsonSerializer进行java类的序列化

    • 通过JsonDeserializer进行反序列化为java类

  • Spring Mvc 中 controller方法的序列化
    • 对于@ResponseBody@RequestBody注解的值,使用RequestResponseBodyMethodProcessor匹配合适的HttpMessageConverter进行序列化/反序列化,其中常用的HttpMessageConverterMappingJackson2HttpMessageConverter 用以对Accept头为application/json 的请求的返回值进行json序列化

    • 对于GET请求中参数方法(简单类型的@RequestParam可以省略),使用RequestParamMethodArgumentResolver并调用TypeConverterDelegate匹配合适的ConversionService进行序列化为方法参数对应的java类型


而我们实现这个组件,就需要从这三种序列化中入手

1.首先,我们先写一个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CustomSerializationEnum {
    Type myBatis();

    Type json() default Type.NAME;

    Type requestParam() default Type.NAME;

    enum Type {
    NAME,ID,CLASS,TO_STRING
    }
}
复制代码

2. 这个注解用于指示这个枚举类如何序列化,稍后我们再详细设置,我们再写一个接口,用于可以详细设置序列化的值,而不是直接使用枚举的原始值,同时,为了更好兼容步骤3中的适配器,我们增加获取原始类和原始对象的方法

import org.springframework.core.annotation.AnnotationUtils;

public interface EnumSerialize<T extends Enum<T> & EnumSerialize<T>> {
    default String getSerializationName() {
        //noinspection unchecked
        return ((Enum<?>) this).name();
    }

    default Integer getSerializationId() {
        //noinspection unchecked
        return ((Enum<?>) this).ordinal();
    }
    /**
     * 获取原始类,专门给适配器使用的
     */
    default Class<T> getOriginalClass() {
        //noinspection unchecked
        return (Class<T>) this.getClass();
    }
    /**
     * 获取原始枚举对象,专门给适配器使用的
     */
    default Enum<T> getOriginalEnum() {
        //noinspection unchecked
        return (Enum<T>) this;
    }
    static <T extends Enum<T> & EnumSerialize<T>> CustomSerializationEnum getAnnotation(Class<T> enumClass) {
        return AnnotationUtils.findAnnotation(enumClass, CustomSerializationEnum.class);
    }
}
复制代码

3. 那么,想必大家也看出来了,实现这个接口才是正路,那么问题来了,对于没有实现这接口的枚举类怎么办呢?我们把它包装在一个适配器中:

@SuppressWarnings("rawtypes")
public final class EnumSerializeAdapter implements EnumSerialize {
    private final Enum<?> enumInstance;

    public Enum<?> getEnumInstance() {
        return enumInstance;
    }

    public EnumSerializeAdapter(Enum<?> enumInstance) {
        this.enumInstance = enumInstance;
    }

    @Override
    public String getSerializationName() {
        return enumInstance.name();
    }

    @Override
    public Integer getSerializationId() {
        return enumInstance.ordinal();
    }

    @Override
    public String toString() {
        return enumInstance.toString();
    }

    @Override
    public Class<?> getOriginalClass() {
        return enumInstance.getClass();
    }

    @Override
    public Enum<?> getOriginalEnum() {
        return enumInstance;
    }
}

复制代码

4. 这样有了接口,有了适配器,我们就能给一开始注解CustomSerializationEnum中的Type添加具体功能了:


import sun.misc.SharedSecrets;

import java.lang.annotation.*;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <H1>指示枚举类自定义序列化的方法,被注解类必须为 <span style="color:#c7c7c7">枚举类</span></H1><br>
 * <H1>被注解类通常实现 <
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java程序员-张凯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值