web-dev-qa-db-ja.com

Java、Classpath、Classloading =>同じjar / projectの複数のバージョン

これは経験豊富なコーダーにとっては馬鹿げた質問かもしれません。しかし、私のプロジェクトで使用される他のフレームワーク/ jarのいくつかが必要とするライブラリ(httpクライアント)があります。しかし、それらはすべて次のような異なるメジャーバージョンを必要とします。

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar

クラスローダーは、何らかの形でそれらを分離するのに十分なインテリジェントですか?ほとんどないでしょうか?クラスが3つすべてのjarで同じ場合、クラスローダーはこれをどのように処理しますか。どれがロードされ、なぜですか?

クラスローダーは1つのjarのみをピックアップしますか、それともクラスを任意に混合しますか?たとえば、クラスがVersion-1.jarからロードされた場合、同じクラスローダーからロードされた他のすべてのクラスはすべて同じjarに入りますか?

この問題をどのように処理しますか?

Jarを「required.jar」に何らかの形で「組み込み」、Classloaderによって「1つのユニット/パッケージ」と見なされる、または何らかの形でリンクされるようにするトリックがありますか?

112
jens

クラスローダー関連の問題は非常に複雑な問題です。いずれにしても、いくつかの事実に留意する必要があります。

  • 通常、アプリケーション内のクラスローダーは、複数のクラスローダーです。 bootstrapクラスローダーは適切なものに委任します。新しいクラスをインスタンス化すると、より具体的なクラスローダーが呼び出されます。ロードしようとしているクラスへの参照が見つからない場合、bootstrapクラスローダーに到達するまで、その親に委任されます。ロードしようとしているクラスへの参照が見つからない場合、ClassNotFoundExceptionが発生します。

  • 同じバイナリ名を持つ2つのクラスがあり、同じクラスローダーで検索可能であり、どちらをロードしているかを知りたい場合は、特定のクラスローダーがクラス名を解決しようとする方法のみを調べることができます。

  • Java言語仕様によれば、クラスのバイナリ名には一意性制約はありませんが、私が見る限り、クラスローダーごとに一意である必要があります。

同じバイナリ名を持つ2つのクラスをロードする方法を見つけることができます。また、デフォルトの動作をオーバーライドする2つの異なるクラスローダーによってそれらのクラス(およびすべての依存関係)をロードする必要があります。大まかな例:

    ClassLoader loaderA = new MyClassLoader(libPathOne);
    ClassLoader loaderB = new MyClassLoader(libPathTwo);
    Object1 obj1 = loaderA.loadClass("first.class.binary.name", true)
    Object2 obj2 = loaderB.loadClass("second.class.binary.name", true);

クラスローダーのカスタマイズは難しい作業でした。可能であれば、互換性のない複数の依存関係を避けることをお勧めします。

52
Luca Putzu

各クラスロードは、1つのクラスのみを選択します。通常、最初に見つかったもの。

OSGi は、同じjarの複数のバージョンの問題を解決することを目的としています。 Equinox および Apache Felix は、OSGiの一般的なオープンソース実装です。

20
Tarlog

クラスローダーは、最初にクラスパスにあるjarからクラスをロードします。通常、ライブラリの互換性のないバージョンはパッケージに違いがありますが、まれに、実際には互換性がなく、置き換えることができません-jarjarを試してください。

6
Alex Gitelman

クラスローダーは、オンデマンドでクラスをロードします。これは、アプリケーションと関連ライブラリが最初に必要とするクラスが他のクラスの前にロードされることを意味します。通常、依存クラスをロードする要求は、依存クラスのロードおよびリンク処理中に発行されます。

クラスローダーの重複したクラス定義に遭遇したことを示すLinkageErrorsに遭遇する可能性があります。通常、どのクラスを最初にロードするかを決定しようとしません(ローダ)。時々、クラスローダーはクラスパスで発生する最初のクラスをロードし、重複するクラスを無視しますが、これはローダーの実装に依存します。

このような種類のエラーを解決するための推奨される方法は、競合する依存関係を持つライブラリの各セットに対して個別のクラスローダーを利用することです。そうすれば、クラスローダーがライブラリからクラスをロードしようとすると、依存クラスは他のライブラリと依存関係にアクセスできない同じクラスローダーによってロードされます。

6
Vineet Reynolds

URLClassLoaderを使用して、diff-2バージョンのjarからクラスをロードする必要があります。

URLClassLoader loader1 = new URLClassLoader(new URL[] {new File("httpclient-v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
URLClassLoader loader2 = new URLClassLoader(new URL[] {new File("httpclient-v2.jar").toURL()}, Thread.currentThread().getContextClassLoader());

Class<?> c1 = loader1.loadClass("com.abc.Hello");

Class<?> c2 = loader2.loadClass("com.abc.Hello");

BaseInterface i1 = (BaseInterface) c1.newInstance();

BaseInterface i2 = (BaseInterface) c2.newInstance();
0
Pankaj Kalra