2013-01-14 13 views
12

私はNameGeneratorというクラスがあります。これを使用して、特定のロジックに従って名前を生成することができます。次に、私はTestNameGenerationクラスを、ユーザーからの手紙を求めて、それに従って名前を生成するメソッドで書きます。今度はNameGenerationクラスのロジックを変更し、アプリケーションを停止せずにその特定の変更を適用したいと思います。Javaで実行中のアプリケーションのクラスを置き換えるにはどうすればいいですか?

私はこれをクラスローダーについて学ぶために行いました。誰かがそのようなことを行うために学ぶ必要のある主要な概念やサイトを参照してください。

答えて

18

ここでは動作テストがあります。 5秒ごとにTest.main()はtest.Test1をリロードします。クラスをファイルシステムから呼び出して、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出力は、これは例えばTomcatがWebアプリケーションを再ロードする方法です

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

を変更し表示されます。 Webアプリケーションごとに個別のClassLoaderがあり、新しいClassLoaderに新しいバージョンがロードされます。古いものは、古いクラスと同様にJavaオブジェクトと同様にGCedされます。

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

+0

助けてくれてありがとう。ここで行ったように、新しいクラスローディングのたびに新しいクラスローダインスタンスがあることで、この問題が解決されました。 –

0

strategy patternについてはどうですか?これは、クラスローダーを使用する代わりに、問題のためのより良い解決策になる可能性があります。これらの記事で

+1

変更されたバージョンのクラスを動的にロードするのはどのように役立ちますか? – Andremoniy

+0

私はstartegyパターンがいくつかの実装の中から選択するのを助けるでしょう、私たちが実行時にインターフェイスにコーディングすることで好むので。しかし、私たちは新しい実装全体を導入することはできません。 –

+0

@Prasadあなたはオンザフライで注入することができます:戦略パターンとDIを使って上記の私の例を参照してください:setNameGeneration(Class.forName( "com.bean.ClasseA")。newInstance()) –

1

は2通りの方法があります。

  1. は、クラスローダを上書きすることができ最初に使って使うアプリケーションをブートストラップするための既存のクラスローダー、特に動的更新が必要なクラスのために、上書きされたクラスローダーを使用する必要があります。
  2. OSGiフレームワークを使用する。アプリケーションの規模に応じて、OSGiフレームワークはコーディング規約に従う必要があるため、良い選択ではないかもしれません。

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

+0

+1 for OSGi。実行時にすぐにバンドルを追加/削除することができます。 – Puce

+0

最初の提案をありがとう。しかし、この小規模なアプリケーションの唯一の意図は、コアJavaクラスの読み込みの概念を学ぶことなので、私はOSGIを使用したくありません。 –

1

を役に立てば幸いNameGeneration と呼ばれるインターフェイスを作成し、変数を定義するクライアント・クラスでインスタンス ために、n個の実装NameGenerationImpl1、NameGenerationimpl2を作成します。

NameGeneration nameGeneration ; 
0を

とセッター:

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

nameGenerationはあなたが望むものを生成します。

あなたは、インスタンスのために行って、実装を変更するアルゴリズムに変更する場合:

setNameGeneration(new NameGenerationImpl1()) ; 

または

setNameGeneration(new NameGenerationImpl2()) ; 
+0

'POO'とは何ですか?理解できません。 –

+0

@モハマド:彼はOOPを意味すると思います... –

+0

これは基本的に戦略パターンです。 –

1

独自のクラスローダを書く:

私はthis 1のようにして成功を収めましたベース thisチュートリアルもご覧ください。

+0

リンクが壊れています。どのようにこれらの記事を見つけるためのアイデア? –

0

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

関連する問題