これを理解するには、Java でクラスとクラスローディングが実際にどのように機能するかを理解する必要があります。「クラスパス」の観点からロードを考えることがよくありますが、これは物事がどのように機能するかを大幅に単純化したものです。多くの Java SE 環境ではこれでうまくいきますが、OSGi のようなマルチテナント環境ではさらに複雑になります。
基本的に、Java クラスのスコープは次の 3 つに限定されます。
- 名前
- パッケージ
- クラスローダー
myPackage.MyClass の 2 つのインスタンスを複数回 JVM にロードすることは完全に可能です。複数のクラスローダーが必要なだけです。これらのクラスは同一の .class ファイルからロードできますが、実行時には異なります。これは、次のようなコードを記述すると、多くの混乱を招く可能性があります。
MyClass c = (MyClass)obj;
を取得しClassNotFoundException: MyClass
ます。
クラスはパッケージに存在し、これに関連する特別な可視性ルールがあります。可視性が明示されていない型、メソッド、およびフィールドは、同じパッケージ内のすべての型に表示されます。クラスがロードされると、java.lang.reflect.Package に関連付けられ、クラスが同じ java.lang.reflect.Package インスタンスに関連付けられていることを本質的にチェックすることによって可視性ルールが解決されます。したがって、mypackage.MyClass の場合、2 つの異なるクラスローダーを使用して 2 回ロードすると、mypackage の 2 つの Package インスタンスが取得されます。
OSGi は、マルチテナント クラスのロードをサポートするように設計されています。これにより、同じ JVM でクラスまたはパッケージの 2 つの異なるバージョンを同時に持つことができます。これにより、異なるバージョンの依存関係がある場合に発生する多くの問題が解決されます。これは、バンドルごとに異なる Classloader を使用して実装されます。フラグメントは、バンドルに関連付けられており、フラグメント内のクラスが独自のクラスローダーを持つのではなく、バンドルのクラスローダーによってロードされるという点で、異なる働きをします。
これを元の質問に関連付けるために、junit テストを (フラグメントではなく) バンドルに入れる場合、クラスは別のクラスローダーによってロードされるため、java.lang.reflect の別のインスタンスに関連付けられます。 JVM がメンバーのアクセス可能性をテストするときに失敗するようにパッケージ化します。