美文网首页
基于Jackson自定义BeanSerializerModifi

基于Jackson自定义BeanSerializerModifi

作者: 离别刀 | 来源:发表于2023-06-19 10:01 被阅读0次

    解决问题:java返回对象中关于枚举、字典数据的自动转化。
    实现思路:
    1、通过自定义注解 对需要转化的字段进行标记,注解中可定义枚举类型,若没有定义枚举则从数据字典获取。
    2、自定义对象的BeanSerializerModifier,对做了标记的字段设置自定义的JsonSerializer。
    3、自定义JsonSerializer的实现。
    4、自定义MappingJackson2HttpMessageConverter,并设置自定义的BeanSerializerModifier为默认处理方式。
    5、将自定义的MappingJackson2HttpMessageConverter加入到HttpMessageConverters中,可以通过重写WebMvcConfigurationSupport.extendMessageConverters的方式实现。
    以下是具体的代码实现,有些地方需要用户根据实际情况自己实现,比如从字典获取数据等。

    自定义注解标记

    import java.lang.annotation.*;
    
    @Inherited
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Dict {
        static enum Void {}
    
        Class<? extends Enum<?>> enumType() default Void.class;
    
        /**
         * 默认值,获取不到字典则使用默认值
         *
         * @return ""
         */
        String defaultValue() default "";
    }
    

    自定义BeanSerializerModifier,以下代码中DictConstants.getDictCacheKey(valueStr)只是自定义key,需要自己实现。

    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.BeanDescription;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializationConfig;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
    import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
    import com.hg.dcm.commons.core.HGBusinessException;
    import com.hg.dcm.commons.core.SpringContextUtil;
    import com.hg.energy.service.RedisService;
    import lombok.extern.slf4j.Slf4j;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Objects;
    
    @Slf4j
    public class DictSerializerModifier extends BeanSerializerModifier {
        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            for (BeanPropertyWriter beanProperty : beanProperties) {
                Dict dict = beanProperty.getAnnotation(Dict.class);
                if (dict != null) {
                    beanProperty.assignSerializer(new DictSerializer(dict));
                }
            }
            return beanProperties;
        }
    
        /**
         * 字典自定义序列化
         */
        static class DictSerializer extends JsonSerializer<Object> {
            /**
             * 生成序列化字段后缀
             */
            private static final String LABEL_SUFFIX = "Desc";
            /**
             * 字典配置信息
             */
            private final Dict dict;
    
            /**
             * 枚举获取key方法
             */
            private static final String[] KEY_METHODS = {"getValue", "getCode", "getStatus", "name"};
            /**
             * 获取枚举描述方法
             */
            private static final String[] DESC_METHODS = {"getDesc"};
    
            /**
             * 构造方法
             *
             * @param dict 字典描述
             */
            public DictSerializer(Dict dict) {
                this.dict = dict;
            }
    
            /**
             * Method that can be called to ask implementation to serialize
             * values of type this serializer handles.
             *
             * @param value    Value to serialize; can <b>not</b> be null.
             * @param gen      Generator used to output resulting Json content
             * @param provider Provider that can be used to get serializers for
             *                 serializing Objects value contains, if any.
             */
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
                provider.defaultSerializeValue(value, gen);
                // 添加转换之后的字段:xxxDesc
                String fieldName = gen.getOutputContext().getCurrentName();
                gen.writeStringField(fieldName.concat(LABEL_SUFFIX), value != null ? this.getDesc(dict, value) : null);
            }
    
    
            /**
             * 获取字典信息
             * TODO
             *
             * @param dict  字典对象
             * @param value 字典值
             * @return
             */
            private String getDesc(Dict dict, Object value) {
                try {
                    // 先查询是否是枚举类型,查到则返回
                    String enumDesc = this.getEnumDesc(dict, value);
                    if (enumDesc != null) {
                        return enumDesc;
                    }
                    String valueStr = Objects.toString(value);
                    //获取缓存key,可以自定义
                    String key = DictConstants.getDictCacheKey(valueStr);
                    // Redis 缓存操作类 这里建议优先使用本地缓存, 本地缓存 -> redis -> Db
                    RedisService redis = SpringContextUtil.getBean(RedisService.class);
                    if (redis.exists(key)) {
                        return redis.get(key);
                    }
                    // 数据库字典操作类
    
                    //redis.setEx(key, desc, 1, TimeUnit.HOURS);
                    return "字典数据";
                } catch (Exception e) {
                    log.error("字典转换:获取字典描述异常,使用默认值:{},key:{}, dict:{}, 异常:{}", dict.defaultValue(), value, dict.enumType(), e.getMessage(), e);
                    return dict.defaultValue();
                }
            }
    
    
            /**
             * 获取枚举类型的描述信息
             *
             * @param dict  字典
             * @param value 值
             * @return 枚举desc字段
             */
            private String getEnumDesc(Dict dict, Object value) throws InvocationTargetException, IllegalAccessException {
                if (dict == null || value == null) {
                    return null;
                }
                Class<? extends Enum<?>> et = dict.enumType();
                if (Dict.Void.class.equals(et)) {
                    return null;
                }
    
                Enum<?>[] enums = et.getEnumConstants();
                Method keyMethod = this.getMethod(et, KEY_METHODS);
                if (keyMethod == null) {
                    // 自定义业务异常
                    throw new HGBusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(KEY_METHODS)));
                }
                Method descMethod = this.getMethod(et, DESC_METHODS);
                if (descMethod == null) {
                    throw new HGBusinessException(String.format("字典转换:枚举:%s,没有方法:%s", et.getName(), Arrays.toString(DESC_METHODS)));
                }
                for (Enum<?> e : enums) {
                    if (value.equals(keyMethod.invoke(e))) {
                        return Objects.toString(descMethod.invoke(e));
                    }
                }
                log.error("字典转换:通过枚举转换失败,枚举:{},值:{},KeyMethod:{},DescMethod:{}", et.getName(), value, Arrays.toString(KEY_METHODS), Arrays.toString(DESC_METHODS));
                throw new HGBusinessException(String.format("字典转换失败,枚举:%s,值:%s", et.getName(), value));
            }
    
            /**
             * 获取读方法
             *
             * @param enumType    枚举类
             * @param methodNames 方法名称
             * @return Method
             */
            private Method getMethod(Class<? extends Enum<?>> enumType, String... methodNames) {
                for (String methodName : methodNames) {
                    try {
                        return enumType.getMethod(methodName);
                    } catch (NoSuchMethodException e) {
                    }
                }
                return null;
            }
        }
    }
    
    

    自定义MappingJackson2HttpMessageConverter

        public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
            SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
            builder.modules(simpleModule);
            builder.serializationInclusion(JsonInclude.Include.NON_NULL);
            return new MappingJackson2HttpMessageConverter(builder.build());
        }
    

    将自定义的MappingJackson2HttpMessageConverter加入到HttpMessageConverters中

    @Configuration
    public class WebConfiguration extends WebMvcConfigurationSupport {
    
    
        @Override
        protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(0,mappingJackson2HttpMessageConverter());
        }
    
    
        public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
            SimpleModule simpleModule = new SimpleModule().setSerializerModifier(new DictSerializerModifier());
            builder.modules(simpleModule);
            builder.serializationInclusion(JsonInclude.Include.NON_NULL);
            return new MappingJackson2HttpMessageConverter(builder.build());
        }
    
    }
    
    

    参考文献:https://www.jianshu.com/p/90d7d101e715

    相关文章

      网友评论

          本文标题:基于Jackson自定义BeanSerializerModifi

          本文链接:https://www.haomeiwen.com/subject/yeceydtx.html