P1 のオンライン事故によるログの行

写真

出典:juejin.cn/post/7156439842958606349

  • オンラインインシデントレビュー

  • シナリオ復元

  • ソースコード分析

    • JavaBeanSerizlier シリアル化の原則

    • シリアル化フローチャート

  • サンプルコード

  • コード仕様

  • 高頻度のシリアル化の 3 つのケース


オンラインインシデントレビュー

少し前に、私の同僚が非常に単純な関数を追加しました。夜にコードが起動される前に、review彼は会社の勤勉で進取的な価値観について考えました。彼は一時的にログの行を追加しました。警察に電話して、すぐにコードをロールバックしてください、問題を見つけてログを追加するコードを削除し、再度オンラインにします。

Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus をベースとしたフロントエンドとバックエンドに分離されたブログ。記事、カテゴリ、タグ管理、ダッシュボードなどの機能をサポートするバックグラウンド管理システムが含まれています。

  • GitHub アドレス: https://github.com/weiwosuoai/WeBlog

  • Gitee アドレス: https://gitee.com/AllenJiang/WeBlog

 
 

シナリオ復元

を定義します CountryDTO

public class CountryDTO {
    private String country;

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCountry() {
        return this.country;
    }

    public Boolean isChinaName() {
        return this.country.equals("中国");
    }
}

テストクラスを定義する FastJonTest

public class FastJonTest {
    @Test
    public void testSerialize() {
        CountryDTO countryDTO = new CountryDTO();
        String str = JSON.toJSONString(countryDTO);
        System.out.println(str);
    }
}

実行時のエラー空指针:

写真

ヌルポインタ

エラー メッセージから、シリアル化プロセス中にメソッドが実行されたことがわかりますがisChinaName()、この時点ではthis.country変数が空であるため、次の問題が発生します。

  • シリアル化はなぜ行われるのですかisChinaName()?

  • さらに言えば、シリアル化プロセス中にどのようなメソッドが実行されるのでしょうか?

ソースコード分析

デバッグを通じて呼び出しリンクのスタック情報を観察します。

写真

写真

写真

写真

呼び出しチェーンでは、クラスASMSerializer_1_CountryDTO.writeはテクノロジーをFastJson使用してasm動的に生成されますASMSerializer_1_CountryDTO

javaasm テクノロジーの使用シナリオの 1 つは、繰り返し実行中のリフレクションのオーバーヘッドを回避するために、リフレクションを置き換えるクラスを動的に生成することです。

JavaBeanSerizlier シリアル化の原則

JavaBeanSerializer以下の図から、シリアル化の過程では主にクラスのメソッドが呼び出されることがわかりますwrite()

写真

ObjectSerializer 実装クラス JavaBeanSerializer

これはJavaBeanSerializer主にgetObjectWriter()メソッドを通じて、getObjectWriter()実行プロセスのデバッグを通じて、より重要なcom.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializerメソッドを見つけて取得され ます。com.alibaba.fastjson.util.TypeUtils#computeGetters

public static List<FieldInfo> computeGetters(Class<?> clazz, //
                                                 JSONType jsonType, //
                                                 Map<String,String> aliasMap, //
                                                 Map<String,Field> fieldCacheMap, //
                                                 boolean sorted, //
                                                 PropertyNamingStrategy propertyNamingStrategy //
    ){
    //省略部分代码....
    Method[] methods = clazz.getMethods();
    for(Method method : methods){
        //省略部分代码...
        if(method.getReturnType().equals(Void.TYPE)){
            continue;
        }
        if(method.getParameterTypes().length != 0){
            continue;
        }
            //省略部分代码...
        JSONField annotation = TypeUtils.getAnnotation(method, JSONField.class);
        //省略部分代码...
        if(annotation != null){
            if(!annotation.serialize()){
                continue;
            }
            if(annotation.name().length() != 0){
                //省略部分代码...
            }
        }
        if(methodName.startsWith("get")){
         //省略部分代码...
        }
        if(methodName.startsWith("is")){
         //省略部分代码...
        }
    }
}

コードから、それは大きく 3 つの状況に分類できます。

  • @JSONField(.serialize = false, name = "xxx")注釈

  • getXxx() : getで始まるメソッド

  • isXxx(): で始まるメソッド

シリアル化フローチャート

写真

シリアル化フローチャート

Spring Boot + MyBatis Plus + Vue 3.2 + Vite + Element Plus をベースとしたフロントエンドとバックエンドに分離されたブログ。記事、カテゴリ、タグ管理、ダッシュボードなどの機能をサポートするバックグラウンド管理システムが含まれています。

  • GitHub アドレス: https://github.com/weiwosuoai/WeBlog

  • Gitee アドレス: https://gitee.com/AllenJiang/WeBlog

 
 

サンプルコード

/**
 * case1: @JSONField(serialize = false)
 * case2: getXxx()返回值为void
 * case3: isXxx()返回值不等于布尔类型
 * case4: @JSONType(ignores = "xxx")
 */
@JSONType(ignores = "otherName")
public class CountryDTO {
    private String country;

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCountry() {
        return this.country;
    }

    public static void queryCountryList() {
        System.out.println("queryCountryList()执行!!");
    }

    public Boolean isChinaName() {
        System.out.println("isChinaName()执行!!");
        return true;
    }

    public String getEnglishName() {
        System.out.println("getEnglishName()执行!!");
        return "lucy";
    }

    public String getOtherName() {
        System.out.println("getOtherName()执行!!");
        return "lucy";
    }

    /**
     * case1: @JSONField(serialize = false)
     */
    @JSONField(serialize = false)
    public String getEnglishName2() {
        System.out.println("getEnglishName2()执行!!");
        return "lucy";
    }

    /**
     * case2: getXxx()返回值为void
     */
    public void getEnglishName3() {
        System.out.println("getEnglishName3()执行!!");
    }

    /**
     * case3: isXxx()返回值不等于布尔类型
     */
    public String isChinaName2() {
        System.out.println("isChinaName2()执行!!");
        return "isChinaName2";
    }
}

操作の結果は次のようになります。

isChinaName()执行!!
getEnglishName()执行!!
{"chinaName":true,"englishName":"lucy"}

コード仕様

戻り値に注意する必要がある場合、パラメータの数に注意する必要がある場合、アノテーションに注意する必要がある場合、@JSONTypeおよび注釈に注意を払う必要があります@JSONField。ナレッジ ポイントの習熟度は異なり、この差異によりコードの問題が発生しやすいため、推奨される解決策を用意するようにしてください。

@JSONField(serialize = false)シリアル化に参加しないメソッドは明示的にマークすることをお勧めします。@JSONField以下はアノテーションを使用した後のコードです。どのメソッドがシリアル化に参加する必要がないのかが一目でわかりますか。

public class CountryDTO {
    private String country;

    public void setCountry(String country) {
        this.country = country;
    }

    public String getCountry() {
        return this.country;
    }

    @JSONField(serialize = false)
    public static void queryCountryList() {
        System.out.println("queryCountryList()执行!!");
    }

    public Boolean isChinaName() {
        System.out.println("isChinaName()执行!!");
        return true;
    }

    public String getEnglishName() {
        System.out.println("getEnglishName()执行!!");
        return "lucy";
    }

    @JSONField(serialize = false)
    public String getOtherName() {
        System.out.println("getOtherName()执行!!");
        return "lucy";
    }

    @JSONField(serialize = false)
    public String getEnglishName2() {
        System.out.println("getEnglishName2()执行!!");
        return "lucy";
    }

    @JSONField(serialize = false)
    public void getEnglishName3() {
        System.out.println("getEnglishName3()执行!!");
    }

    @JSONField(serialize = false)
    public String isChinaName2() {
        System.out.println("isChinaName2()执行!!");
        return "isChinaName2";
    }
}

高頻度のシリアル化の 3 つのケース

写真

高頻度のシリアル化の 3 つのケース

基本的には、問題発見→原理解析→問題解決→昇華(プログラミング仕様)という流れになります。

  • ビジネスに焦点を当てる: 問題の解決 -> 適切なソリューションの選択方法 -> 適切なソリューションでシステム アプリケーションを拡張する方法。

  • テクノロジーに焦点を当てる: 1 つの問題を解決し、1 つの問題に沿ってこの方針の原則を習得します。

しかし実際のところ、私はこのコードに満足していません。FastJson に依存しすぎているからです。私が望む効果は、特定の JSON シリアル化フレームワークに依存しないことです。交換が必要なときはいつでも交換できます。

また、コードを記述するときは、ログに頼りすぎないでください。ログには、すべてのログではなく、重要かつ重要な情報のみを入力する必要があります。私は、1 時間で 128G ディスクいっぱいの管理システムを実行するシステムを見たことがあります。同時実行性はほとんどありませんが、ほぼすべてのリクエストで数 M 個のログが出力されます。これについては後で別途説明します。

@JSONFieldや などの機能アノテーションについては@JSONType、後でチーム内で標準化して新しいデカップリング ソリューションを提供し、削除します。

 
 

 

 

おすすめ

転載: blog.csdn.net/2301_78588786/article/details/131870419