3

いくつかのベンダーAPIの上に構築されるJavaプロジェクトを作成したいと思います。APIは、上記のベンダーのソフトウェアを実行しているサーバーに接続し、さまざまなアクションを実行します。

2つの異なるAPIバージョンでサポートされている2つの異なるバージョンのサーバーがあります。APIへの変更は、内部実装のみにあります。IE古いバージョンで使用できるクラス、インターフェイス、メソッドなどは、新しいバージョンに存在します。したがって、私が作成するコードは、どちらのAPIバージョンでもコンパイルおよび実行する必要があります。APIを使用して接続するときにサーバーに提示されるAPIにはバージョン番号があり、その特定のサーバーで異なるバージョンのAPIを使用することはできません。

  1. 実行時にJARファイルをオンザフライで切り替える方法はありますか?(ac / c ++ DLLのようなもの??)

  2. 実行時にAPIバージョンを切り替えることができない場合、問題を処理するための最も洗練された方法は何ですか。コードを2x(APIバージョンごとに1つ)ビルドしますか?

何かが足りないことを願っていますが、アプローチ2は理想的ではないようです。理由のより具体的な例を次に示します。

package org.myhypotheticalwrapper.analyzer;

import org.myhypothetical.worker;
import org.myhypothetical.comparator;

public class Analyzer {

    Worker w1 = new Worker();
    Worker w2 = new Worker();

    Comparator c = new Comparator(w1.connectAndDoStuff(),w2.connectAndDoStuff());
    c.generateReport();
}

これが私のジレンマです。w1を古いAPIでビルドし、w2を新しいAPIでビルドして、適切なサーバーに接続できるようにします。それらが上にあるAPIを除いて、それらは同じです(同一のコード)。コードが同一であっても、単に異なるAPIバージョンに対応するために、W1とW2の2つの一意の名前のクラスタイプを作成する必要がありますか?やりとりしたいAPIバージョンがたくさんあると、手に負えなくなる可能性があります。

どんな提案やコメントも大歓迎です。

-新しい男

4

4 に答える 4

4

最も簡単なのは、おそらくデフォルトのクラスパスにないクラスにクラスローダーをロードすることです。

http://www.exampledepot.com/egs/java.lang/LoadClass.htmlから

    // Convert File to a URL
    URL url = file.toURL();          // file:/c:/myclasses/
    URL[] urls = new URL[]{url};

    // Create a new class loader with the directory
    ClassLoader cl = new URLClassLoader(urls);

    // Load in the class; MyClass.class should be located in
    // the directory file:/c:/myclasses/com/mycompany
    Class cls = cl.loadClass("com.mycompany.MyClass");
于 2009-07-22T18:40:29.150 に答える
2

一度ロードされたクラスファイルを実際に変更することはできないため、実行時にクラスを置き換える方法は実際にはありません。JavaRebelのようなプロジェクトは、javaagentを介したインストルメンテーションの巧妙な使用でこれを回避することに注意してください。ただし、それを使用して実行できることは限られています。

そのサウンドから、環境内に2つの並列実装を同時に持つ必要があり、実行時にクラスをリロードする必要はありません。これは非常に簡単に実行できます。ランタイムが次のファイルで構成されていると想定します。

  • Analyzer.jar-これには、上記のアナライザー/テストコードが含まれています
  • api.jar-これは一般的な前向きのAPIコードです(例:インターフェース)
  • api-impl-v1.jar-これは実装の古いバージョンです
  • api-impl-v2.jar-これは実装の新しいバージョンです

ワーカーインターフェイスコードが次のようになっていると仮定します。

package com.example.api;

public interface Worker {
  public Object connectAndDoStuff();
}

また、実装(v1とv2の両方)は次のようになります。

package com.example.impl;

import com.example.api.Worker;

public class WorkerImpl implements Worker {
  public Object connectAndDoStuff() {
    // do stuff - this can be different in v1 and v2
  }
}

次に、次のようにアナライザーを作成できます。

package com.example.analyzer;

import com.example.api.Worker;

public class Analyzer {
  // should narrow down exceptions as needed
  public void analyze() throws Exception {  
    // change these paths as need be
    File apiImplV1Jar = new File("api-impl-v1.jar"); 
    File apiImplV2Jar = new File("api-impl-v2.jar");

    ClassLoader apiImplV1Loader = 
      new URLClassLoader(new URL[] { apiImplV1Jar.toURL() });
    ClassLoader apiImplV2Loader = 
      new URLClassLoader(new URL[] { apiImplV2Jar.toURL() });

    Worker workerV1 = 
      (Worker) apiImplV1Loader.loadClass("com.example.impl.WorkerImpl")
                              .newInstance();
    Worker workerV2 = 
      (Worker) apiImplV2Loader.loadClass("com.example.impl.WorkerImpl").
                              .newInstance();

    Comparator c = new Comparator(workerV1.connectAndDoStuff(),
                                  workerV2.connectAndDoStuff());
    c.generateReport();
  }
}

アナライザーを実行するには、クラスパスにanalyzer.jarとapi.jarを含めますが、api-impl- v1.jarとapi-impl-v2.jarは省略します。

于 2009-07-22T19:00:27.753 に答える
0

クラスが異なるパッケージに含まれていることを確認する必要があります。同じパッケージリストを持つ2つのjarファイルをインポートして、一方が他方よりも認識されることを期待することはできません。それらが異なるパッケージに含まれている場合は、次を使用できます。

com.foo.Worker w1;
com.bar.Worker w2;
于 2009-07-22T18:42:08.117 に答える
0

ワーカーには、インターフェイスを実装するデリゲートが必要です。古いAPI用と新しいAPI用の2つのデリゲートを作成する必要があります。実行時にインスタンス化するデリゲートを選択します。何かのようなもの:

public class Worker {
private WorkerDelegate delegate;

public void foo() { delegate.foo(); }

public Object bar(){ return delegate.bar(); }

public Enum API{v1,v2};

public Worker(API api) {
try { switch(api){
case v1: delegate = Class.forName("my.v1.impl").newInstance(); break
case v2: delegate = Class.forName("my.v2.impl").newInstance(); break
}
}catch(...){throw new Error(e);}
}

}

後で簡単に実装を追加できます。

于 2009-07-22T19:02:17.873 に答える