コードとセットアップの詳細を知らなければ、確信することは不可能ですが、カスタムクラスローダーを試してみたときに遭遇した問題を思い出させてくれます。
シナリオは次のとおりです。
- ブートローダー(JVMプロパー/ Tomcat /いずれか)がコードをロードします
- クラスローダーは、上記のクラスパスでは利用できない追加をロードします。
- あなたのコードはそれらの追加を参照しています。
- これらの追加は、ブートローダーによってロードされたコードと同じ名前空間では使用できません。
- ブートローダーの名前空間のコードが実行され、カスタム名前空間のコードを参照しようとしますが、これはブートローダーの名前空間からは見えません。したがって、その時点で、JVMはNoClassDefFoundエラーでベイルします。
この理由は、クラスローダーの階層が一方向でのみ機能するためです。つまり、サブ名前空間(子クラスローダー)のコードは、より広い親名前空間(親クラスローダー)では使用できません(表示されません)。そして、このシステムにハッキングする良い方法はありません。
幸い、親名前空間で使用できるインターフェイスを定義してから、サブ名前空間でのみ表示されるコードでこれらを実装できます。次に、親名前空間内にのみ存在するコードは、キャストとメソッド呼び出しの目的でこのインターフェイス名を使用できるため、サブ名前空間コンポーネントにアクセスできます。
これを機能させるには、カスタムクラスローダーが、親ローダーがアクセスできない(つまり、クラスパスの外にある)コンポーネントだけでなく、これらのコンポーネントと直接インターフェイスするコードのビットもロードするようにする必要があります。これらのシンボル(タイプ名/メソッドなど)を明示的に参照してください。そうしないと、これらのコンポーネントへの参照が親の名前空間になり(ブートクラスローダーがデフォルトですべての独自のコードをロードすることを忘れないでください)、元の問題に戻ります。
これを行うには、目的のクラスローディング委任モデルを破壊します。通常、クラスを自分でロードする前に、親ローダーに任せます。ただし、ここで、コードが親ローダーで使用できないこれらのコンポーネントのいずれにも触れていないことを事前に確認する必要があります。最も簡単な方法は、おそらく、クラスローダーがクラス名のセットを維持するようにコードパスを設定することです。これは、親ローダーにロードさせるのではなく、それ自体をロードすることです。
どういうわけかカスタムクラスローダーに伝える方法を見つける必要があります。これには、クラス宣言の型アノテーションを使用できます。ここでの考え方は、クラスローダーが親によってロードされたクラスをイントロスペクトし、タイプ名に特定のカスタムアノテーションが見つかった場合、アノテーションのメソッドを呼び出して、親ローダーに許可してはならないクラスシンボルの名前を取得することです。ロード。
例:
@MyCustomAnnotation(implementation="my.custom.package.MyImpl")
public class MyFeatureProvider {
public MyFeature getFeature() { // return an instance of MyImpl here }
}
クラスは以前MyFeatureProvider
にロードされるため、クラスローダーはのアノテーションについて事前に認識しているため、MyImplのデフォルトの委任モデルをオーバーライドできることに注意してください。コードの残りの部分は、親ローダーのインスタンスとしてのみ対話するため、未定義のシンボルを確認する必要はありません。ClassNoDefFoundエラーは解決されます。 MyImpl
MyFeatureProvider
MyImpl
MyFeature