我正在使用apache felix osgi。当我使用classLoader.loadClass(..)
加载课程时,它正在提供ClassNotFoundException
但是当我使用Class.forName()
加载一个类时它工作正常。
当我们使用classLoader.loadClass()
时,它会有什么不同?为什么我们只需要使用Class.forName()
或如何正确使用classLoader.loadClass()
?
答案 0 :(得分:10)
在任何模块化环境(例如OSGi)中,仅按名称加载类是不够的,因为许多模块可能具有具有该名称的类。因此,必须使用应加载它的类名 AND 来唯一标识类。
如果以单arg形式调用Class.forName()
,那么您将无法提供模块信息,因此Java将回退到使用调用者的类加载器。这并不仅仅是随机猜测。在你的情况下,它恰好工作(它找到了课程),但只是通过纯粹的运气!
如果你打电话给ClassLoader.loadClass()
- 请注意这不是静态方法 - 那么你实际上是在提供一个类加载器。这要好得多!不幸的是,你提供了错误的加载器,即不是真正了解该类的加载器。你没有说明你从哪里得到这个装载机所以我只能推测它为什么是错误的。
最好使用OSGi的Bundle.loadClass()
方法,它允许您从包中显式加载类。您需要知道该类应该来自哪个包,但这是在模块化环境中工作的必然结果。
最后,更好的是完全忘记动态类加载并学习如何使用服务。您永远不必在OSGi中使用动态类加载,除非在处理需要它的第三方或遗留库时。
答案 1 :(得分:1)
方法Class.forName(String)使用调用者的类加载器。例如,如果你正在做
class MyClass {
void someMethod() {
Class.forName("my.pkg.SomeClass");
}
}
然后加载类MyClass的类加载器也加载类" my.pkg.SomeClass"。在独立应用程序中,这通常是所谓的系统类加载器。
如果您看到使用Class.forName和ClassLoader.loadClass之间存在差异,那么您正在使用另一个类加载器。
OSGI中的类加载更加困难,因为OSGI能够很好地为所有OSGI包分离类加载器,以免加载所有资源和类的冲突。
答案 2 :(得分:0)
ClassLoader.loadClass
或Class.forName
似乎是同一基本操作的同义词:请求动态类加载。然而,调用Class.forName
执行额外的“检查”,这不是非常有用(当然在OSGi中)。
执行动态类加载时,返回的类型没有代码隐含的类型。代码必须使用反射来访问静态成员或创建实例。创建的实例可以反映或转换为必须已由代码隐式知道的类型。此转换将导致运行时类型检查,这将确保类型安全。
这与VM完成的用于解析类常量池条目的隐式类加载非常不同。由于这些隐式类加载的目标是避免运行时类型检查,因此加载器约束用于确保类型安全。
对某些动态类加载请求强加加载器约束检查似乎没有必要或合理。也就是说,代码调用Class.forName
(或ClassLoader.loadClass
)和解析类常量池条目的VM具有不同的类型安全需求。前者不需要加载器约束检查,因为如果需要,它将在运行时由类型转换完成。后者确实需要加载器约束检查以避免运行时类型检查。
因此,改变Class.forName
行为以避免加载器约束检查似乎是合理的。只有内部VM
类加载请求需要检查加载器约束。
更多了解Click here
答案 3 :(得分:0)
作为附加提示:由于您获得了异常,因此您可能通过调用classLoader
获得了您引用为Thread.currentThread().getContextClassLoader()
的类加载器。在OSGi环境中,它可能是最糟糕的类加载器,因为它是绝对未定义的设置。加载可能一次失败并在另一时间成功,导致难以发现的问题。