JAVAホットアップデート

序章

知識を蓄えるために、最初にこの記事をお読みください: JAVA Instrument

この場合、このInstrumentメカニズムを使用して、単純なホット アップデート ケースを実装します。

全体的な手順は次のとおりです。

  1. premainメソッドを含むパッケージを作成しますjarこの方法では、定期的にファイルを検出し、ホット アップデートを実行します。
  2. パラメータは、コマンド ラインからビジネス クラスを開始するときに使用されます-javaagentjava -javaagent:jarpath[=选项] Main

インターネット上にはMavenパッケージングjarを使用するケースがたくさんありますが、ここでは純粋なコマンド ライン アプローチについて話します。

コード

構造は次のとおりです

ここに画像の説明を挿入
これはコンパイルされた構造です。ロジックの実装から始めましょう。

1.premainメソッドを使用してプロキシ クラスを作成する

まずpremainメソッドを含むクラスを宣言します。クラス名を次のように設定しますAgent。このクラスは最初にスレッドを開始し、このスレッドは
25 秒後にUserクラスを置き換えます。
その中でも、ファイルをバイト単位で読み取って置換することはInstrument関連知識です。

package com.wyw;

import java.io.File;
import java.io.FileInputStream;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.util.concurrent.TimeUnit;

public class Agent {
    
    
    public static void premain(String arg, Instrumentation instrumentation) {
    
    
        System.out.println("premain starting ~");

        Thread thread = new Thread(() -> {
    
    
            try {
    
    
                TimeUnit.SECONDS.sleep(2);
                Class<?> person = Class.forName("com.wyw.User");
                File file = new File("C:\\Users\\QTZ\\IdeaProjects\\hotfix\\src\\com\\wyw\\User.class");
                FileInputStream fileInputStream = new FileInputStream(file);
                byte[] bytes = fileInputStream.readAllBytes();
                instrumentation.redefineClasses(new ClassDefinition(person, bytes));
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        });
        thread.start();
    }
}

2. ビジネスクラスを書く

メインの起動クラス:
名前付きクラスを宣言しwywUserこのクラスのプロパティを 50 秒以内に出力します。

package com.wyw;

import java.util.concurrent.TimeUnit;

public class Main {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        User user = new User("wyw");
        int i = 0;
        while (i < 50) {
    
    
            TimeUnit.SECONDS.sleep(1);
            System.out.println(i++ + ": " + user.getName());
        }
    }
}

User クラスは次のように定義されます。

package com.wyw;

public class User {
    
    
    private static final String DEFAULT_PREFIX = "User: ";

    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return DEFAULT_PREFIX + name;
    }
}

パッケージの作成jar、クラスのコンパイル

javacまず、上記の実装を次のようにコンパイルする必要があります。

PS C:\Users\QTZ\IdeaProjects\hotfix\src> javac .\com\wyw\User.java .\com\wyw\Agent.java .\com\wyw\Main.java

実行後、いくつかのファイルが生成されることがわかりますclass

次に、このパッケージを作成するには、パッケージ化用のリストを宣言する必要があります。その中でパスをjar指定する必要があります。このリストの名前は次のとおりですPremain-ClassMANIFEST.MF

Manifest-Version: 1.0
Premain-Class: com.wyw.Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

次に、コマンドラインで次のようにします。

PS C:\Users\QTZ\IdeaProjects\hotfix\src> jar -c -f agent.jar -m .\com\wyw\MANIFEST.MF

実行後に が生成されますagent.jar

これら 2 つの基本的な手順が完了したら、クラスを開始します。

PS C:\Users\QTZ\IdeaProjects\hotfix\src> java -javaagent:agent.jar Main

コンソールは次のような出力を生成します。

PS C:\Users\QTZ\IdeaProjects\hotfix\src> java -javaagent:agent.jar com.wyw.Main
premain starting ~
0: User: wyw
1: User: wyw
2: User: wyw
3: User: wyw
4: User: wyw

このケースのコード ロジックでは、クラスは 25 秒後に置き換えられます。つまり、Userその期間中にクラスを変更してコンパイルする必要があります。
たとえば、次のUserように変更します。

package com.wyw;

public class User {
    
    
    private static final String DEFAULT_PREFIX = "!!!User: ";

    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return DEFAULT_PREFIX + name;
    }
}

次に、それをコンパイルします。

PS C:\Users\QTZ\IdeaProjects\hotfix\src> javac .\com\wyw\User.java

コンソール出力は次のように表示されます。

17: User: wyw
18: User: wyw
19: User: wyw
20: User: wyw
21: User: wyw
22: User: wyw
23: User: wyw
24: !!!User: wyw
25: !!!User: wyw
26: !!!User: wyw
27: !!!User: wyw
28: !!!User: wyw
29: !!!User: wyw

つまり、置き換えは成功です。

ホット アップデートではメソッドを追加または削除できないことに注意してください。

オリジナルをUser次の方法で置き換えます。

package com.wyw;

public class User {
    
    
    private static final String DEFAULT_PREFIX = "User: ";

    private String name;

    public User(String name) {
    
    
        this.name = name;
    }

    public String getName() {
    
    
        return addMethod() + DEFAULT_PREFIX + name;
    }

    private String addMethod() {
    
    
        return "Add: ";
    }
}

クラスを再コンパイルした後、実行時に次のエラーが報告されます。

20: User: wyw
21: User: wyw
22: User: wyw
23: User: wyw
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
        at java.instrument/sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
        at java.instrument/sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:193)
        at com.wyw.Agent.lambda$premain$0(Agent.java:28)
        at java.base/java.lang.Thread.run(Thread.java:834)
24: User: wyw
25: User: wyw

要約する

通常、ホット アップデートを通じてメソッドを追加または削除することはできませんが、メソッドの内部実装を変更することはできます。
このケースは、関連するコード ロジックを単純に示しており、ホット アップデートと置換には固定時間と固定クラスが使用されます。
特定のプロジェクトに投影すると、そのロジックに従って現地の状況に応じてロジックを実装したり、何らかの仕様を策定したりする必要があります。

ここでさらに私の考えをいくつか述べます。

いつチェックするか

ここでは定期的にチェックしていますが、エンジニアリングでは定期的にチェックするように修正する必要があります。

どのクラスを更新する必要があるかを判断する

ここで私は死に向かって直接書きます。他のアプローチには次のようなものがあります。

  • ファイルを使用してホット アップデートが必要なパスを保存し、このファイルを読み取ります。
  • 現在のクラスの変更時刻を保存し、時刻が変更されたときに更新します。

おすすめ

転載: blog.csdn.net/sayWhat_sayHello/article/details/121083133