详解Java如何优雅的实现字典翻译

 更新时间:2023年04月27日 11:17:57   作者:李同学  
当我们在Java应用程序中需要对字典属性进行转换返回给前端时,如何简单、方便、并且优雅的处理是一个重要问题。在本文中,我们将介绍如何使用Java中的序列化机制来优雅地实现字典值的翻译,从而简化开发

什么是序列化

在Java中,序列化是将对象转换为字节流的过程,可以将这些字节流保存到文件中或通过网络进行传输。反序列化是将字节流转换为原始对象的过程。通过序列化和反序列化,我们可以在不同的应用程序之间传递对象,也可以将对象保存到文件中以便以后使用。

使用序列化实现字典值的翻译

在Java中,我们可以使用序列化机制来实现编码与其对应的含义的对应关系。具体步骤如下:

1.定义一个字典注解与,例如:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DictSerializer.class)
public @interface Dict {
​
    /**
     * 字典类型
     * 比如在描述学生的时候,1代表小学生 2代表初中生 3代表高中生 4代表大学生
     * 同样在描述老师的时候,1代表语文老师 2代表数学老师 3代表英语老师 4代表体育老师
     * 同样的数值在不同类型下,代表含义不同,所以需要指定字典的类型
     */
    String dic();
}

2.自定义注解结合继承JsonSerialize实现ContextualSerializer,实现返回结果转译:

@Slf4j
public class DictSerializer extends StdSerializer<Object> implements ContextualSerializer {
​
    private transient String dictCode;
​
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty beanProperty){
        Dict dict = beanProperty.getAnnotation(Dict.class);
        return createContextual(dict.dic());
    }
​
    private JsonSerializer<?> createContextual(String dicCode) {
        DictSerializer serializer = new DictSerializer();
        serializer.setDictCode(dicCode);
        return serializer;
    }
​
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider){
​
        String dictCode = getDictCode();
        if (StrUtil.isBlank(dictCode)) {
            return;
        }
        if (Objects.isNull(value)) {
            return;
        }
        try {
            // 因为序列化是每个对象都需要进行序列话操作,这里为了减少网络IO,使用了 guava 的本地缓存(代码在下面)
            Map<String, String> dictMap = DictionaryConstants.DICTIONARY_CACHE.get(dictCode);
            if (dictMap.containsKey("nullValue")) {
                // 当本地缓存中不存在该类型的字典时,就调用查询方法,并且放入到本地缓存中(代码在下面)
                dictMap = translateDictValue(dictCode);
                DictionaryConstants.DICTIONARY_CACHE.put(dictCode, dictMap);
            }
            // 通过数据字典类型和value获取name
            String label = dictMap.get(value.toString());
            gen.writeObject(value);
            // 在需要转换的字段上添加@Dict注解,注明需要引用的code,后端会在返回值中增加filedName_dictText的key,前端只需要取对应的 filedName_dictText 就可以直接使用
            gen.writeFieldName(gen.getOutputContext().getCurrentName() + DictionaryConstants.DICT_TEXT_SUFFIX);
            gen.writeObject(label);
        } catch (Exception e) {
            log.error("错误信息:{}", e.getMessage(), e);
        }
    }
​
    private String getDictCode() {
        return dictCode;
    }
​
    private void setDictCode(String dictCode) {
        this.dictCode = dictCode;
    }
​
    protected DictSerializer() {
        super(Object.class);
    }
}

3.将同类型的字典编码和对应的含义保存到一个Map中,例如:

private Map<String, String> translateDictValue(String code) {
    if (StrUtil.isBlank(code)) {
      return null;
    }
    // Map<String, String> map = new HashMap<>();
    // map.put("1", "小学生");
    // map.put("2", "初中生");
    // map.put("3", "高中生");
    // map.put("4", "大学生");
  
    // 因为我们公司采用微服务,然后字典模块单独拆分成一个服务,所以这里使用Feign方式调用
    DictionaryFeignClient dictionaryFeign = SpringUtil.getBean(DictionaryFeignClient.class);
    return dictionaryFeign.dictionary(code);
}

4.因为序列化是需要每个对象都进行序列话操作,如果返回的是集合的话,就会进行很多次序列化操作,此时就需要对相同类型的字典进行缓存,我这里使用了guava 的 LoadingCache 进行本地缓存(这里可能有人会说了,如果这个时候字典值对应的含义修改了,你这个缓存岂不是会导致数据不正确,首先字典功能一般是管理端进行增删改操作,而且字典一旦定好了是不会轻易修改的,如果你要硬杠,你赢了)。

public class DictionaryConstants {
​
    /**
     * 字典翻译文本后缀
     */
    public static final String DICT_TEXT_SUFFIX = "_dictText";
​
    public static final LoadingCache<String, Map<String, String>> DICTIONARY_CACHE = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(30, TimeUnit.SECONDS)
            .expireAfterAccess(10, TimeUnit.SECONDS)
            .build(new CacheLoader<String, Map<String, String>>() {
                @Override
                public Map<String, String> load(String key) {
                    Map<String, String> map = new HashMap<>();
                    map.put("nullValue", "nullValue");
                    return map;
                }
            });
}

这里额外补充一个小知识:

  • expireAfterWrite和expireAfterAccess都是Google Guava缓存库中的缓存过期策略。
  • expireAfterWrite表示缓存项在指定时间后过期,无论缓存项是否被访问过。例如,如果我们将缓存项的expireAfterWrite设置为10分钟,则缓存项在被添加到缓存中10分钟后过期,无论它是否被访问过。
  • 这两种过期策略可以单独或组合使用,以实现更灵活的缓存策略。例如,我们可以将缓存项的expireAfterWrite设置为10分钟,同时将expireAfterAccess设置为5分钟,这样缓存项将在10分钟后过期,或者在最近5分钟内没有被访问时过期,以先到者为准。
  • 使用expireAfterWrite和expireAfterAccess可以避免缓存中的数据过期时间过长或过短,从而提高缓存的效率和可靠性。

5.相比于使用 aop 切面的方式,使用序列化的方式能更好的进行字典的翻译(因为 aop 方式很难处理对象中的属性的属性),例如:

public class Company {
  private List<Staff> staffs;
}
​
public class Staff {
  private Integer age;
  private String name;
  @Dic(dic = "position")
  private String position;
  
}

在这种场景中,如果返回的是 Company 集合,使用 aop 切面方式就很难达到(开发难度与开发成本)与序列化方式同样的效果。

通过以上步骤,我们可以使用Java中的序列化机制来优雅地实现字典编码与其对应的含义的对应关系,从而简化编码数据的管理和维护。

总结

在本文中,我们介绍了如何使用Java中的序列化机制来实现编码与其对应的含义的对应关系,从而简化编码数据的管理和维护。通过序列化和反序列化,我们可以高效地传递和保存对象,提高应用程序的效率和可维护性。

到此这篇关于详解Java如何优雅的实现字典翻译的文章就介绍到这了,更多相关Java字典翻译内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java贪吃蛇游戏编写代码

    java贪吃蛇游戏编写代码

    这篇文章主要为大家详细介绍了java贪吃蛇游戏的编写代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • java排序去重示例分享

    java排序去重示例分享

    这篇文章主要介绍了java排序去重示例,对String strs = "ZZZ BBB AAA OOO ZZZ AAA ZZZ"计算出现个数,排序去重,需要的朋友可以参考下
    2014-02-02
  • Java面试题之HashSet的实现原理

    Java面试题之HashSet的实现原理

    这篇文章主要介绍了Java面试题之HashSet的实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • Java用Arrays.fill()初始化二维数组的实现

    Java用Arrays.fill()初始化二维数组的实现

    这篇文章主要介绍了Java用Arrays.fill()初始化二维数组的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 详解spring boot 以jar的方式启动常用shell脚本

    详解spring boot 以jar的方式启动常用shell脚本

    本篇文章主要介绍了详解spring boot 以jar的方式启动常用shell脚本,具有一定的参考价值,有兴趣的可以了解一下
    2017-09-09
  • 精通Java List 按字段提取对象

    精通Java List 按字段提取对象

    这篇文章主要介绍了精通Java List 按字段提取对象的相关资料,需要的朋友可以参考下
    2023-11-11
  • 解析JDK14中的java tools简介

    解析JDK14中的java tools简介

    这篇文章主要介绍了JDK14中的java tools简介,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • 关于@PropertySource配置的用法解析

    关于@PropertySource配置的用法解析

    这篇文章主要介绍了关于@PropertySource配置的用法解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java包装类的缓存机制原理实例详解

    Java包装类的缓存机制原理实例详解

    这篇文章主要介绍了Java包装类的缓存机制原理实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java通过正则表达式捕获组中的文本

    Java通过正则表达式捕获组中的文本

    这篇文章主要给大家介绍了关于利用Java如何通过正则表达式捕获组中文本的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧下
    2019-09-09

最新评论