ホームページ  >  記事  >  Java  >  Java で辞書翻訳をエレガントに実装する方法

Java で辞書翻訳をエレガントに実装する方法

PHPz
PHPz転載
2023-05-12 17:31:061450ブラウズ

シリアル化とは

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. 同じタイプの辞書コードと対応する意味をマップに保存します (例:

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分後に期限切れになります。

  • これら 2 つの有効期限戦略を単独で使用することも、組み合わせて使用​​して、より柔軟なキャッシュ戦略を実現することもできます。たとえば、キャッシュ アイテムの期限切れ後書き込みを 10 分に設定し、期限切れ後アクセスも 5 分に設定すると、キャッシュ アイテムは 10 分後に期限切れになるか、過去 5 分間アクセスされなかった場合に期限切れになるかのどちらか早い方になります。 。

  • expireAfterWrite とexpirateAfterAccess を使用すると、キャッシュ内のデータの有効期限が長すぎたり短すぎたりすることを回避できるため、キャッシュの効率と信頼性が向上します。

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。