4

Tomcat用のカスタムクラスローダーを実装しようとしています。私の最初の試みはクラスキャスト例外をもたらしました(どうやら、tomcatは私のローダーをorg.apache.catalina.loader.WebappLoaderにキャストしようとします)。了解しました。WebappLoaderを拡張し、catalina.jarをビルドパスに追加しました。

これで、展開する準備ができました(私は思います)。このエラーが発生します:

重大:Catalina.start:LifecycleException:開始:: java.lang.NoClassDefFoundError:org / apache / catalina / loader / WebappLoader

Tomcatには実行用のcatalina.jarが付属しているので、99.9%はすでにtomcatにロードされていると確信しています。これを確認するために、apacheWebappLoaderが含まれている/server/lib/catalina.jarを確認しました。さらに、で別のcatalina.jarを手動でリンクすると、予想どおり、問題が全体的に混乱します。

よくわかりません。どんなヒントも熱くなるでしょう。

ありがとう!

更新:興味深いことに、tomcat6(WebappLoaderの拡張、tomcat5.5で動作)でも同じことが発生し、ClassCastExceptionが発生します。キャストを実行するクラスが、クラスをロードしたローダーとは異なるローダーを使用してロードされたように聞こえます。どこかに別の不足しているTomcat構成がない限り、とにかくそれをどのように制御できるかわかりません。tomcat6のアイデアもありますか?

4

2 に答える 2

5

多分私は密集していますが、それはWebappLoaderではなくWebappClassLoaderであるべきだと思います。ただし、インポートは問題ないようです。

于 2010-12-27T01:00:58.350 に答える
1

コードとセットアップの詳細を知らなければ、確信することは不可能ですが、カスタムクラスローダーを試してみたときに遭遇した問題を思い出させてくれます。

シナリオは次のとおりです。

  1. ブートローダー(JVMプロパー/ Tomcat /いずれか)がコードをロードします
  2. クラスローダーは、上記のクラスパスでは利用できない追加をロードします。
  3. あなたのコードはそれらの追加を参照しています。
  4. これらの追加は、ブートローダーによってロードされたコードと同じ名前空間では使用できません。
  5. ブートローダーの名前空間のコードが実行され、カスタム名前空間のコードを参照しようとしますが、これはブートローダーの名前空間からは見えません。したがって、その時点で、JVMはNoClassDefFoundエラーでベイルします。

この理由は、クラスローダーの階層が一方向でのみ機能するためです。つまり、サブ名前空間(子クラスローダー)のコードは、より広い親名前空間(親クラスローダー)では使用できません(表示されません)。そして、このシステムにハッキングする良い方法はありません。

幸い、親名前空間で使用できるインターフェイスを定義してから、サブ名前空間でのみ表示されるコードでこれらを実装できます。次に、親名前空間内にのみ存在するコードは、キャストとメソッド呼び出しの目的でこのインターフェイス名を使用できるため、サブ名前空間コンポーネントにアクセスできます。

これを機能させるには、カスタムクラスローダーが、親ローダーがアクセスできない(つまり、クラスパスの外にある)コンポーネントだけでなく、これらのコンポーネントと直接インターフェイスするコードのビットもロードするようにする必要があります。これらのシンボル(タイプ名/メソッドなど)を明示的に参照してください。そうしないと、これらのコンポーネントへの参照が親の名前空間になり(ブートクラスローダーがデフォルトですべての独自のコードをロードすることを忘れないでください)、元の問題に戻ります。

これを行うには、目的のクラスローディング委任モデルを破壊します。通常、クラスを自分でロードする前に、親ローダーに任せます。ただし、ここで、コードが親ローダーで使用できないこれらのコンポーネントのいずれにも触れていないことを事前に確認する必要があります。最も簡単な方法は、おそらく、クラスローダーがクラス名のセットを維持するようにコードパスを設定することです。これは、親ローダーにロードさせるのではなく、それ自体をロードすることです。

どういうわけかカスタムクラスローダーに伝える方法を見つける必要があります。これには、クラス宣言の型アノテーションを使用できます。ここでの考え方は、クラスローダーが親によってロードされたクラスをイントロスペクトし、タイプ名に特定のカスタムアノテーションが見つかった場合、アノテーションのメソッドを呼び出して、親ローダーに許可してはならないクラスシンボルの名前を取得することです。ロード。

例:

@MyCustomAnnotation(implementation="my.custom.package.MyImpl")
public class MyFeatureProvider {
  public MyFeature getFeature() { // return an instance of MyImpl here }
}

クラスは以前MyFeatureProviderにロードされるため、クラスローダーはのアノテーションについて事前に認識しているため、MyImplのデフォルトの委任モデルをオーバーライドできることに注意してください。コードの残りの部分は、親ローダーのインスタンスとしてのみ対話するため、未定義のシンボルを確認する必要はありません。ClassNoDefFoundエラーは解決されます。 MyImplMyFeatureProviderMyImplMyFeature

于 2010-12-26T22:21:31.303 に答える