Java怎么优雅的实现字典翻译

其他教程   发布日期:2025年04月05日   浏览次数:85

这篇文章主要介绍了Java怎么优雅的实现字典翻译的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java怎么优雅的实现字典翻译文章都会有所收获,下面我们一起来看看吧。

什么是序列化

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

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

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

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

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

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

  1. @Slf4j
  2. public class DictSerializer extends StdSerializer<Object> implements ContextualSerializer {
  3. private transient String dictCode;
  4. @Override
  5. public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty beanProperty){
  6. Dict dict = beanProperty.getAnnotation(Dict.class);
  7. return createContextual(dict.dic());
  8. }
  9. private JsonSerializer<?> createContextual(String dicCode) {
  10. DictSerializer serializer = new DictSerializer();
  11. serializer.setDictCode(dicCode);
  12. return serializer;
  13. }
  14. @Override
  15. public void serialize(Object value, JsonGenerator gen, SerializerProvider provider){
  16. String dictCode = getDictCode();
  17. if (StrUtil.isBlank(dictCode)) {
  18. return;
  19. }
  20. if (Objects.isNull(value)) {
  21. return;
  22. }
  23. try {
  24. // 因为序列化是每个对象都需要进行序列话操作,这里为了减少网络IO,使用了 guava 的本地缓存(代码在下面)
  25. Map<String, String> dictMap = DictionaryConstants.DICTIONARY_CACHE.get(dictCode);
  26. if (dictMap.containsKey("nullValue")) {
  27. // 当本地缓存中不存在该类型的字典时,就调用查询方法,并且放入到本地缓存中(代码在下面)
  28. dictMap = translateDictValue(dictCode);
  29. DictionaryConstants.DICTIONARY_CACHE.put(dictCode, dictMap);
  30. }
  31. // 通过数据字典类型和value获取name
  32. String label = dictMap.get(value.toString());
  33. gen.writeObject(value);
  34. // 在需要转换的字段上添加@Dict注解,注明需要引用的code,后端会在返回值中增加filedName_dictText的key,前端只需要取对应的 filedName_dictText 就可以直接使用
  35. gen.writeFieldName(gen.getOutputContext().getCurrentName() + DictionaryConstants.DICT_TEXT_SUFFIX);
  36. gen.writeObject(label);
  37. } catch (Exception e) {
  38. log.error("错误信息:{}", e.getMessage(), e);
  39. }
  40. }
  41. private String getDictCode() {
  42. return dictCode;
  43. }
  44. private void setDictCode(String dictCode) {
  45. this.dictCode = dictCode;
  46. }
  47. protected DictSerializer() {
  48. super(Object.class);
  49. }
  50. }

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

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

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

  1. public class DictionaryConstants {
  2. /**
  3. * 字典翻译文本后缀
  4. */
  5. public static final String DICT_TEXT_SUFFIX = "_dictText";
  6. public static final LoadingCache<String, Map<String, String>> DICTIONARY_CACHE = CacheBuilder.newBuilder()
  7. .maximumSize(1000)
  8. .expireAfterWrite(30, TimeUnit.SECONDS)
  9. .expireAfterAccess(10, TimeUnit.SECONDS)
  10. .build(new CacheLoader<String, Map<String, String>>() {
  11. @Override
  12. public Map<String, String> load(String key) {
  13. Map<String, String> map = new HashMap<>();
  14. map.put("nullValue", "nullValue");
  15. return map;
  16. }
  17. });
  18. }

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

  • expireAfterWrite和expireAfterAccess都是Google Guava缓存库中的缓存过期策略。

  • expireAfterWrite表示缓存项在指定时间后过期,无论缓存项是否被访问过。例如,如果我们将缓存项的expireAfterWrite设置为10分钟,则缓存项在被添加到缓存中10分钟后过期,无论它是否被访问过。

  • 这两种过期策略可以单独或组合使用,以实现更灵活的缓存策略。例如,我们可以将缓存项的expireAfterWrite设置为10分钟,同时将expireAfterAccess设置为5分钟,这样缓存项将在10分钟后过期,或者在最近5分钟内没有被访问时过期,以先到者为准。

  • 使用expireAfterWrite和expireAfterAccess可以避免缓存中的数据过期时间过长或过短,从而提高缓存的效率和可靠性。

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

  1. public class Company {
  2. private List<Staff> staffs;
  3. }
  4. public class Staff {
  5. private Integer age;
  6. private String name;
  7. @Dic(dic = "position")
  8. private String position;
  9. }

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

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

以上就是Java怎么优雅的实现字典翻译的详细内容,更多关于Java怎么优雅的实现字典翻译的资料请关注九品源码其它相关文章!