原文地址:https://www.yangbajing.me/2019/04/17/spring应用enum处理/
在Spring应用开发中,Java枚举(enum)默认都是使用字符串进行序列化/反序列化的。都通常我们都想将其序列化/反序列化为int值。
MyBatis MyBatis-plus提供了插件用于自定义enum的序列化/反序列化,非常方便。只需要在application.properties
配置文件中指定默认的枚举处理器即可,配置如下:
1 mybatis-plus.configuration.default-enum-type-handler: com.baomidou.mybatisplus.extension.handlers.EnumTypeHandler
枚举类需要实现IEnum接口或将字段标记@EnumValue
注解,这样MyBatis-plus在遇到相关枚举类型时就会通过指定的配置来序列/反序列化。
Jackson 序列化 Jackson的配置要相对复杂一点,Jackson对序列化提供了默认的支持,在要使用的字段上加JsonValue
注解即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public enum UserStatusEnum implements IEnum <Integer > { DISABLE(0 , "禁用" ), NORMAL(1 , "正常" ), PLAIN(999 , "普通" ); @JsonValue private Integer value; private String name; UserStatusEnum(Integer value, String name) { this .value = value; this .name = name; } public String getName () { return name; } public Integer getValue () { return value; } }
反序列化 从int反序列化到enum,需要自定义IEnumDeserializer
反序列化器,代码如下:
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 public class EnumDeserializers extends Deserializers .Base { @Override public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException { if (IEnum.class.isAssignableFrom(type)) { return new IEnumDeserializer(EnumResolver.constructUnsafe(type, config.getAnnotationIntrospector())); } return super .findEnumDeserializer(type, config, beanDesc); } } public class IEnumDeserializer extends StdScalarDeserializer <IEnum <Integer >> implements ContextualDeserializer { private final IEnum<Integer>[] enums; private final EnumResolver enumResolver; public IEnumDeserializer (EnumResolver byNameResolver) { super (byNameResolver.getEnumClass()); this .enumResolver = byNameResolver; this .enums = (IEnum<Integer>[]) enumResolver.getRawEnums(); } @Override public IEnum<Integer> deserialize (JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { int value = p.getIntValue(); return Arrays.stream(this .enums).filter(e -> e.getValue() == value).findFirst() .orElseThrow(() -> new JsonParseException(p, "枚举需要为整数类型" )); } @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { return this ; } }
并实现Jackson Module:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MyModule extends Module { @Override public String getModuleName () { return "MyModule" ; } @Override public Version version () { return Version.unknownVersion(); } @Override public void setupModule (SetupContext context) { context.addDeserializers(new EnumDeserializers()); } }
反序列化器和Jackson Module定义好后就需要把它加入Jackson里了,有两种方式:
手动注册: 获取objectMapper
,将MyModule
注册到Jackson。
objectMapper.registerModule(new MyModule());
自动注册: 通过Java自带的ServiceLoader
服务提供商加载机制来自动注册模块到Jackson。在resources
资源目录创建服务配置文件,文件路径如下:
1 2 3 resources META-INF.services com.fasterxml.jackson.databind.Module
服务配置文件内容:
1 com.fasterxml.jackson.module.yangbajing.MyModule
自定义Spring Boot Jackson
反序列化器、Jackson模块都写好了,我们需要自定义Spring Boot来启动Jackson的模块自动注册功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @EnableWebFlux @Configuration public class WebConfiguration implements WebFluxConfigurer { @Autowired private ObjectMapper objectMapper; @Override public void configureHttpMessageCodecs (ServerCodecConfigurer configurer) { ServerCodecConfigurer.ServerDefaultCodecs defaultCodecs = configurer.defaultCodecs(); defaultCodecs.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper)); defaultCodecs.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper)); } @Bean @Order(-1) public ObjectMapper objectMapper (Jackson2ObjectMapperBuilder builder) { return builder.createXmlMapper(false ).build().findAndRegisterModules(); } }
findAndRegisterModules()
方法将通过ServiceLoader
机制从classpath路径中找到所有的Module
并注册到Jackson。
在void configureHttpMessageCodecs(ServerCodecConfigurer configurer)
函数中自定义ServerDefaultCodecs
,使用新的ObjectMapper
来注册jackson2JsonEncoder
和jackson2JsonDecoder
。
小结 Java提供了丰富而完善的enum机制,但大部化序列化/反序列化工具都使用文字 来对其进行序列化/反序列化。而通常我们都会枚举序列化/反序列化为int。
Spring还是一个优秀的框架的,从公司/组织层面选择它不会错。
可以在 https://github.com/yangbajing/spring-reactive-sample 找到示例代码。