web-dev-qa-db-ja.com

Javaで実行中のアプリケーションのクラスを置き換える方法は?

NameGeneratorという名前のクラスがあるとします。これを使用して、特定のロジックに従って名前を生成できます。次に、ユーザーからの手紙を要求し、それに応じて名前を生成するメソッドを使用してTestNameGenerationクラスを記述します。ここで、NameGenerationクラスのロジックを変更し、アプリケーションを停止せずにその特定の変更を適用します。

私はクラスローダーの詳細を学ぶためにこれを行いました、そして誰かがそのようなことをするために学ぶ必要がある主要な概念を説明したり、参照をサイトに載せたりできますか?

20
Prasad Weera

これが動作テストです。 5秒ごとにTest.main()はファイルシステムからtest.Test1.classをリロードし、Test1.hello()を呼び出します

package test;

public class Test1 {
    public void hello() {
        System.out.println("Hello !");
    }
}

public class Test {

    static class TestClassLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            if (name.equals("test.Test1")) {
                try {
                    InputStream is = Test.class.getClassLoader().getResourceAsStream("test/Test1.class");
                    byte[] buf = new byte[10000];
                    int len = is.read(buf);
                    return defineClass(name, buf, 0, len);
                } catch (IOException e) {
                    throw new ClassNotFoundException("", e);
                }
            }
            return getParent().loadClass(name);
        }
    }

    public static void main(String[] args) throws Exception {
        for (;;) {
            Class cls = new TestClassLoader().loadClass("test.Test1");
            Object obj = cls.newInstance();
            cls.getMethod("hello").invoke(obj);
            Thread.sleep(5000);
        }
    }
}

それを実行します。次に、Test1を変更して再コンパイルします。

System.out.println("Hello !!!");

テストの実行中。 Test1.helloの出力が変更されたことがわかります

...
Hello !
Hello !
Hello !!!
Hello !!!

これが、TomcatがWebアプリケーションをリロードする方法です。 Webアプリケーションごとに個別のClassLoaderがあり、新しいClassLoaderに新しいバージョンをロードします。古いクラスは、他のJavaオブジェクトおよび古いクラスと同様に、GCされます。

TestClassLoaderを使用してTest1をロードし、リフレクションを使用して最初のメソッドを呼び出したことに注意してください。しかし、すべてのTest1依存関係は暗黙的にTest1クラスローダーで読み込まれます。つまり、すべてのTest1アプリケーションがJVMによってTestClassLoaderに読み込まれます。

30

2つの方法があります。

  1. 使用しているクラスローダーを上書きするには、まず既存のクラスローダーを使用してbootstrapアプリケーションを動的に更新する必要があるクラスに限定して、上書きしたクラスローダーを使用する必要があります。
  2. OSGiフレームワークを使用します。アプリケーションの規模によっては、OSGiフレームワークはコーディング規約に従う必要があるため、適切な選択ではない場合があります。

それが役に立てば幸い

2
Wins

これは本当に簡単です。OOPの利点を利用し、インターフェイスを使用します。クラスローダーを管理する必要はありません。セッターを使用して実装を注入できます。

NameGenerationというインターフェースを作成します。n個の実装NameGenerationImpl1、NameGenerationimpl2などを作成します。クライアントクラスで変数を定義します。

NameGeneration nameGeneration ;

そしてセッター:

 public void setNameGeneration(NameGeneration nameGeneration) {
 this.nameGeneration = nameGeneration ;
}

nameGenerationは、必要なものを生成します。

アルゴリズムを変更するときは、たとえば次のようにして実装を変更します。

setNameGeneration(new NameGenerationImpl1()) ;

または

setNameGeneration(new NameGenerationImpl2()) ;

エンタープライズレベルのアプリケーションで作業していて、クラスを変更するたびにアプリケーションコンテキストが再読み込みされないようにしたい場合は、 [〜#〜] dcevm [〜#〜] および HotSwapAgent を使用しますあなたをより良く助けることができます。以下のコマンドは、DCEVMをJDKに追加します。

Java -jar DCEVM-8u181-installer.jar

そして、以下のVM引数をRunコマンド(または)構成に追加する必要があります

-XXaltjvm = dcevm -javaagent:Your_directory_location/hotswap-agent-1.3.0.jar .jar = autoHotswap = true

リファレンス: https://dzone.com/articles/hot-swap-Java-bytecode-on-runtime

0
Peru

独自のカスタムクラスローダーを作成できます。クラスファイルまたはクラスファイルを含むリソース/ jarに変更がある場合(タイムスタンプを確認)、以前のクラスローダーインスタンスを破棄し、新しいクラスローダーインスタンスを作成して、新しいクラスローダーインスタンスをロードします。

0
Abhay Yadav

戦略パターン はどうですか?クラスローダーを使用するよりも、問題のより良い解決策になる可能性があります。

0
Uwe Plonus