要理解这一点,您需要了解类和类加载在 Java 中的实际工作方式。通常我们会根据“类路径”来考虑加载,但这是对工作方式的极大简化。在许多 Java SE 环境中,这只是工作,但在像 OSGi 这样的多租户环境中,它变得更加复杂。
本质上,在 Java 中,类的作用域包括三件事:
- 姓名
- 包裹
- 类加载器
完全有可能在 JVM 中多次加载 myPackage.MyClass 的两个实例,您只需要多个类加载器。即使这些类可以从相同的 .class 文件中加载,它们在运行时也会有所不同。当您编写这样的代码时,这可能会引起很多混乱:
MyClass c = (MyClass)obj;
并得到一个ClassNotFoundException: MyClass
.
类存在于包中,并且有与之相关的特殊可见性规则。没有明确可见性的类型、方法和字段对同一包中的所有类型都是可见的。当一个类被加载时,它与一个 java.lang.reflect.Package 相关联,可见性规则通过检查这些类是否与同一个 java.lang.reflect.Package 实例相关联来解决。因此,对于 mypackage.MyClass,如果您使用两个不同的类加载器加载它两次,您将获得 mypackage 的两个 Package 实例。
OSGi 旨在支持多租户类加载,这允许您在同一个 JVM 中同时拥有两个不同版本的类或包。这解决了如果您有不同的版本依赖关系而导致的许多问题。它通过为每个包使用不同的类加载器来实现这一点。片段的工作方式与捆绑包不同,因为它们与捆绑包相关联,并且其中的类由捆绑包的类加载器加载,而不是拥有自己的类加载器。
如果你把你的junit测试放在一个包中(而不是一个片段),把这个与你原来的问题联系起来,你的类将由不同的类加载器加载,因此它将与java.lang.reflect的不同实例相关联。打包以便当 JVM 测试成员可访问性时它会失败。