首頁  >  文章  >  Java  >  Java怎麼優雅的實作字典翻譯

Java怎麼優雅的實作字典翻譯

PHPz
PHPz轉載
2023-05-12 17:31:061420瀏覽

什麼是序列化

在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怎麼優雅的實作字典翻譯的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除