0

タスクと初期調査

1 つの Java Swing アプリケーションで 2 つの Oracle Coherence ニア キャッシュ インスタンスを設定しようとしています。解決策のアイデアはここにあります。私のケースはもう少し複雑で、ここからゲームが始まります。

簡単な説明

私の場合、アカウント サービスがあります。SIT と UAT の 2 つのエンドポイントを持つことができます。このようなサービスを 2 つ作成するには、システム変数 ( tangosol.coherence.cacheconfig ) でエンドポイントをオーバーライドするために、Coherence の 2 つの「インスタンス」をロードする必要があります。

私は持っている:

  • アプリのメイン コードは mainapp.jar にあります。
  • account-interfaces.jar にあるAccountServiceインターフェース。
  • account-impl.jar にあり、AccountServiceインターフェースを実装するAccountServiceImplクラス。
  • 私の主なアプリケーションは次の構造を持っています
    bin: startup.bat, startup.sh
    conf: app.properties
    lib: mainapp.jar, account-interfaces.jar, account-impl.jar, coherence.jar
    

試みたアプローチ

私は専用の子優先クラスローダー - InverseClassLoader を作成し、AppLaunchClassLoader (デフォルトの Thread.currentThread().GetContextClassLoader() クラスローダー) を親にしました。InverseClassLoader を使用して、AccountServiceImpl クラスをロードします。

Class<AccountServiceImpl> acImplClass = contextClassLoader.selfLoad(AccountServiceImpl.class).loadClass(AccountServiceImpl.class);
Constructor<AccountServiceImpl> acConstructor =
acImplClass .getConstructor(String.class); 
AccountService acService = acConstructor .newInstance(serviceURL);

問題と質問

  1. 「AccountServiceImpl を AccountService にキャストできません」という例外が発生します。これは、これら 2 つのクラスが異なるクラスローダーによってロードされたことを意味します。しかし、それらのクラスローダは親子関係にあります。クラスが親 (インターフェース - 'abstract' タイプ) によってロードされたとしても、子クラスローダーによってロードされたクラス (concrete impl) では使用できないというのは正しいですか? では、なぜこの親子関係が必要なのでしょうか。
  2. コードで AccountService インターフェイスを指定したところ、デフォルトのクラスローダーによって読み込まれました。上記のコードをスレッドにラップして、コンテキストクラスローダーである InverseClassLoader を設定しようとしました。何も変わっていません。そのようなインターフェイス実装コーディング (通常のコーディング) を使用できず、常にリフレクションを使用して具体的なメソッドを常に呼び出す必要があるというのは正しいですか? (解決策があることを願っています);
  3. たとえば、InverseClassLoader によって読み込まれる AccountService クラスと AccountServiceImpl クラスの両方をリストしました。これら 2 つのクラスからアクセスできる他のクラスを、InverseClassLoader によってもロードする必要がある場合はどうすればよいでしょうか? すべての「関連する」クラスを同じクラスローダーでロードする必要があると言う方法はありますか?

アップデート

InverseClassLoader は次のとおりです。

public class InvertedClassLoader extends URLClassLoader {

private final Set<String> classesToNotDelegate = new HashSet<>();

public InvertedClassLoader(URL... urls) {
    super(urls, Thread.currentThread().getContextClassLoader());
}

public InvertedClassLoader selfLoad(Class<?> classToNotDelegate) {
    classesToNotDelegate.add(classToNotDelegate.getName());
    return this;
}

@Override
public Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    if (shouldNotDelegate(className)) {
        System.out.println("CHILD LOADER: " + className);
        Class<?> clazz = findClass(className);
        if (resolve) {
            resolveClass(clazz);
        }
        return clazz;
    }
    else {
        System.out.println("PARENT LOADER: " + className);
        return super.loadClass(className, resolve);
    }
}

public <T> Class<T> loadClass(Class<? extends T> classToLoad) throws ClassNotFoundException {
    final Class<?> clazz = loadClass(classToLoad.getName());
    @SuppressWarnings("unchecked")
    final Class<T> castedClass = (Class<T>) clazz;
    return castedClass;
}

private boolean shouldNotDelegate(String className) {
    if (classesToNotDelegate.contains(className) || className.contains("tangosol") ) {
        return true;
    }
    return false;
}
4

1 に答える 1

0

問題 1、パート 1 を再現できません (以下を参照)。パート 2 について: クラスローダーの階層は、「X を X にキャストできない」という例外を防ぐためのものです。しかし、親優先のルールを破ると、問題が発生する可能性があります。

問題 2 について: スレッドのコンテキスト クラスローダーを設定しても、それ自体は何もしません。背景については、この記事(javaworld.com) も参照してください。また、問題 1、パート 2 に関連して、現在のクラスローダーとスレッドのコンテキスト クラスローダーの間に親子関係がない場合に何が起こるかを説明する記事からの引用:

クラスをロードして定義するクラスローダーは、そのクラスの内部 JVM ID の一部であることに注意してください。現在のクラスローダがクラス X をロードし、それがたとえばタイプ Y のデータの JNDI ルックアップを実行する場合、コンテキストローダは Y をロードして定義できます。この Y 定義は、同じ名前の定義とは異なりますが、現在のローダ。あいまいなクラス キャストとローダー制約違反の例外を入力します。

以下は、別のクラスローダーからのインターフェースへのキャストが機能することを示す簡単なデモプログラムです (bin-folder 内のクラスとInvertedClassLoader同じ (テスト) パッケージ内の質問からの単純な Java プロジェクトを使用していることに注意してください)。 :

import java.io.File;

public class ChildFirstClassLoading {

    public static void main(String[] args) {

        InvertedClassLoader cl = null;
        try {
            File classesDir = new File(new File("./bin").getCanonicalPath());
            System.out.println("Classes dir: " + classesDir);
            cl = new InvertedClassLoader(classesDir.toURI().toURL());
            cl.selfLoad(CTest.class);
            System.out.println("InvertedClassLoader configured.");
            new CTest("Test 1").test();
            ITest t2 = cl.loadClass(CTest.class)
                    .getConstructor(String.class)
                    .newInstance("Test 2");
            t2.test();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cl != null) {
                try { cl.close(); } catch (Exception ignored) {}
            }
        }
    }

    public interface ITest {
        void test();
    }

    public static class CTest implements ITest {

        static {
            System.out.println("CTest initialized.");
        }

        private String s;
        public CTest(String s) {
            this.s = s;
        }
        public void test() {
            System.out.println(s);
        }
    }

}

に変更ITest t2 =するCTest t2 =と、「CTest を CTest にキャストできません」という例外が発生しますが、インターフェイスを使用するとその例外が回避されます。この小さなデモは問題なく動作するため、アプリケーションでさらに多くのことが起こっており、何らかの形でクラスの読み込みが中断されていると推測されます。クラスローディングが機能する状況から作業し、クラスローディングが中断されるまでコードを追加し続けることをお勧めします。

InvertedClassLoader「子ファーストクラスローダー」によく似ています。この方法のクラスローディングについて説明するいくつかの良い回答については、この質問を参照し てください。子の最初のクラスローダーを使用して、「関連するクラス」(3 番目の問題から) を個別にロードできます。InvertedClassLoader特定のパッケージのクラスを常に「自己ロード」するように を更新することもできます。そして、「クラスローダーによってクラスがロードされると、そのクラスローダーを使用して、必要な他のすべてのクラスをロードする」ことを覚えておいてください (このブログ記事からの引用)。

于 2016-01-20T03:48:56.277 に答える