2

これが私が遭遇している問題です。Java 1.3 で実行され、MyAPI v1.0 などの外部 API を使用する巨大なレガシー アプリケーションがあります。MyAPI 1.0 の正確な実装は、アプリが使用するクラスパスのどこかにあります。そのアプリが外部コードを使用できるようにするメカニズムもあります (ある種のプラグイン メカニズム)。現在、MyAPI v2.0 (v1.0 との 100% 下位互換性はありません) を使用する別の Java ライブラリ (MyLib.jar) があり、そのプラグイン メカニズムを使用して元のアプリから使用する必要があります。そのため、どうにかして、同じ API の 2 つの (互換性のない!) バージョンを連携させる必要があります。具体的には、API クラスが MyLib.jar クラスから呼び出される場合は MyAPI v2.0 を使用し、それ以外の場合はすべて MyAPI 1.0 を使用したいと考えています。

MyAPI 1.0 はクラスパスにあるので、デフォルトで使用されます。それで問題ありません。MyAPI 2.0 からクラスをロードする独自のバージョンのクラス ローダーを作成できます。問題ありません。しかし、どうすればすべてを合わせることができますか?質問:

  1. MyLib.jar オブジェクトは、MyAPI 2.0 からのクラスの多くの (!) インスタンスをインスタンス化します。これは、これらすべてのインスタンス化をリフレクション (自分のクラスローダーを指定) を介して行う必要があるということですか? とんでもない作品だ!

  2. MyAPI 2.0 オブジェクトの一部がインスタンス化され、MyAPI から別のオブジェクトを内部的にインスタンス化する場合、どのクラスローダーを使用しますか? クラスローダーまたはデフォルトのクラスローダーを使用しますか?

  3. 一般的に、私のアプローチは合理的に聞こえますか? より良い方法はありますか?

4

4 に答える 4

3

2番目の質問に答えることから始めましょう:あるクラスから別のクラスに参照するとき、元のクラスをロードしたのと同じクラスローダーによってロードされます(もちろん、クラスローダーがクラスの検索に成功しなかった場合を除き、クラスローダーはそのクラスに委任します親クラスローダー)。

そうは言っても、MyLib.jar全体がカスタムクラスローダーによってロードされないのはなぜですか。そうすれば、通常の方法で新しいバージョンのAPIを参照できます。そうしないと、オブジェクトタイプとリフレクションを最後まで処理する必要があるため、問題が発生します。

于 2009-02-05T12:48:56.420 に答える
2

クラスローダーには注意が必要です。あなたが提案していたことを実行すると、MyAPI 2.0 用のクラス ローダーを使用している場合でも、ほとんどの場合 MyAPI 1.0 になります。その理由は、クラス ローダーを使用してクラスをロードする方法にあります。クラスは常に、最初に親クラス ローダーからロードされます。

「ClassLoader クラスは委任モデルを使用して、クラスとリソースを検索します。ClassLoader の各インスタンスには、関連付けられた親クラス ローダーがあります。クラスまたはリソースの検索が要求されると、ClassLoader インスタンスは、クラスまたはリソースの検索をその親に委任します。クラスまたはリソース自体を見つけようとする前に、クラス ローダーを呼び出します。「ブートストラップ クラス ローダー」と呼ばれる仮想マシンの組み込みクラス ローダーは、それ自体は親を持ちませんが、ClassLoader インスタンスの親として機能する場合があります。" ( http: //java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html )

2 つの API を適切に分離するには、2 つのクラス ローダー (またはメイン アプリケーションに加えて 2 つ) が必要です。

Parent - System classloader
  |- Classloader1 - Used to load MyAPI 1.0
  |- Classloader2 - Used to load MyAPI 2.0

今あなたの質問に。おそらくやりたいことは、API を使用するほとんどのロジックをクラスローダーに移動することです。MyAPI 1.0/2.0 に加えて、それらを使用するアプリケーションの一部をロードする必要があります。次に、親アプリケーションは API を使用するメソッドを呼び出すだけです。このように、単一のリフレクション呼び出しを行ってアプリケーションを開始すると、そのアプリケーション内のすべてが標準参照を使用するだけになります。

于 2009-02-05T12:41:32.363 に答える
0

これは、リフレクションなしで派手なClassLoaderを使用して行うことができます。

基本的に、クラスローダーは「ロードクラスがこのjarからのものである場合は、クラスパスBからロードし、それ以外の場合はメインのClassLoaderを使用する」と言う必要があります。

それよりも少し複雑ですが、そのアイデアから始めれば、うまくいくでしょう。

于 2009-02-05T12:43:07.353 に答える
0

これは合理的に聞こえます。[loadClassのAPIjavadoc][1]には、次のように書かれています。

"指定されたバイナリ名でクラスをロードします。このメソッドのデフォルトの実装は、次の順序でクラスを検索します。findLoadedClass(String)を呼び出して、クラスがすでにロードされているかどうかを確認します。

親クラスローダーでloadClassメソッドを呼び出します。親がnullの場合、代わりに仮想マシンに組み込まれているクラスローダーが使用されます。

findClass(String)メソッドを呼び出して、クラスを検索します。」

CL1がMyAPI1用、CL2がMyAPI2用、CL3がMyLib用の場合、CL3、CL2、CL1の順にチェックするように聞こえます。上記の引用から(親が最初にチェックされるため)、CL1に親CL2を、CL2に親CL3を持たせたいことを示唆しています。親クラスローダーを持つコンストラクターは保護されているため、親が適切に設定されたURLクラスローダーを使用する必要があります。

何かのようなもの

URLCLassLoader cl3 = new URLClassLoader(new URL[]{ path to MyLib});
URLCLassLoader cl2 = new URLClassLoader(new URL[]{ path to API2}, cl3);
URLCLassLoader cl1 = new URLClassLoader(new URL[]{ path to API1}, cl2);

次に、どこでもcl1を使用します。

[1]: http: //java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html#loadClass (java.lang.String 、boolean)

于 2009-02-05T12:43:47.367 に答える