一般的な考え方:動的にクラスを再読み込みして実装を変更できるようにするため、プログラム全体を再起動せずにメインアプリケーションを実行し、 。すべての外部コードは「モジュール」でグループ化され、各モジュールには「onEnable、postEnable、onDisable」というエントリ/出口ポイントを持つメインクラスがあり、任意の数のクラスで構成できます。モジュールをロードするには、エントリポイントを含むクラスが指定され、ロードされます。私はそれらを "モジュール"と "追加のクラス"として参照します。 "モジュール"は上記の関数を含むクラスです。 "public interface Module"、 "additional classes"は、モジュールが使用するすべてのものを指します。実行可能ではありませんが、それ自体でモジュールではありません(たとえば、 "Car implements Module"というモジュールがあり、そのモジュールには "Engine"クラスが必要です) - > "Car"はモジュール、 "Engine"私が最初にモジュールをロードするためにやっているの)URLClassLoader同じフォルダ内にXはありますが、Yは見つかりません
コード(名前はパス、後記の例)を含めた完全なクラス名を含む文字列です:
Class<?> clazz = mainLoader.loadClass(name);
Module module = (Module) clazz.newInstance();
addLoadedModule(module);
enableLoadedModule(module);
そして、ここではそれがだとき、私はモジュールをリロードする方法ですすでに存在する私は実装をオーバーライドすることができます。 "m"は、再ロードされるはずのModuleの現在の実装のインスタンスです。
boolean differs = false;
Class<?> newClass = null;
try (URLClassLoader cl = new URLClassLoader(urls, mainLoader.getParent()))
{
// Try to load the class and check if it differs from the already known one
newClass = cl.loadClass(m.getClass().getName());
differs = m.getClass() != newClass;
}
catch (IOException | ClassNotFoundException e)
{
// Class couldn't be found, abort.
e.printStackTrace();
return;
}
if (!differs)
{
// New class == old class -> no need to reload it
return;
}
Module module = null;
try
{
// Try to instantiate the class
module = (Module) newClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
// Can't instantiate, abort
e.printStackTrace();
return;
}
// Check versions, only reload if the new implementation's version differs from the current one. Version is a custom annotation, don't worry about that; the version check works fine
Version oldVersion = m.getClass().getAnnotation(Version.class);
Version newVersion = module.getClass().getAnnotation(Version.class);
if (oldVersion.equals(newVersion))
{
return;
}
// And if everything went well, disable and remove the old module from the list, then add and enable the new module.
disableModule(m);
modules.remove(m);
modules.put(module, false);
enableLoadedModule(module);
これはmainLoaderで、URLは[]負荷に外部のクラスを含む場所を指すURLです:
mainLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
私はRE-ロード実装しようとすると問題があること、起こります
クラスAのモジュールには、クラスBが機能する必要があります。これは、クラスAを動的にロードしてリロードしようとしたときに起こります。
ロードA - > "確かに、Bが必要です。" - >自動的にBを読み込みます - > "これでいいのよ、Aは今はうまくいく" リロードA - > "もちろん、Bが必要です。" - >クラッシュBは
どちらのクラスがまったく同じフォルダに、このような構造に配置されている見つけることができなかったので:
- クラスAモジュールを実装しています:COM/fooの/バー/ A.class
- クラスB:COM/fooの/バー/ B.class
- のURL:[ "COM/fooの/バー/"]
は、私は負荷の関数を呼び出す( "com.foo.bar.A" )は、最初にロードしようとすると動作しますが、リロードしようとすると失敗します。上記で説明した。
「単一クラスモジュール」をロードしようとするとうまく動作しますが、モジュールが追加の外部クラスに依存しているときに問題が発生します。私は別のクラスローダーを使用してURLClassLoaderのリロードプロセスの親として使用しようとしました。これらのクラスローダーは、sysloader、Module.class.getClassLoader()、mainLoader(その1つを使用して、既に新しいクラス定義を見つけることはありませんそのことを知っているからドライブから再びロードしようとすることさえありません)、mainLoader.getParent()、古いモジュールのクラスローダー、およびモジュールクラスローダーの親です。
私は恐らく何かを目の当たりにしているだけですが、最初に "余分な"クラスをロードするのはなぜか分かりませんが、ベースクラスをリロードすると失敗します...
デバッグ出力や正確なエラーが必要な場合は、デバッグ出力を何が何をするのかを説明するコメントに置き換えたので、何が起こっているのかについてかなり詳細なログが得られましたが、チェックとロードのプロセス全体がうまくいくので、モジュールを有効にしようとするとクラッシュします。モジュールの "onEnable"メソッドは追加のクラスBを必要とします、それは失敗するところです。私が言ったように、クラスAとクラスBの実装が必要な場合、モジュール、他のコードやデバッグ出力が私に知らせて、要求通りにそれらを追加します。
これは問題の「答え」ではありませんが、「この共有スペースクラスは、ロードされたクラスローダが到達不能になるまで、この共有スペースクラスはアンロードされません」というヒントが含まれています。 - >私のコードでは、クラスローダーはtry/catch節の間だけ存在し、最初は新しいモジュールのクラスを取得します。クラスをロードするとすぐに閉じられ、削除されるので、明らかにほかのクラスをロードできなくなります。私は今クラスローダーを保存し、新しいモジュールがロードされたときに閉じて、 "追加クラス"をロードすることができます – Pepich1851
wow ...クラスローダーが範囲外になった問題 –