【问题标题】:How to access a class from Class.forName when that class has a different class loader当该类具有不同的类加载器时如何从 Class.forName 访问该类
【发布时间】:2021-11-09 05:33:38
【问题描述】:

我的情况如下:

我有两个 scala 应用程序,app 1 和 app 2。两者都使用 sbt。我在 App 2 的 sbt shell 中运行“publishLocal”,然后将 App 2 设为 app 1 中的库(我将其添加为 build.sbt 中的库依赖项)。在应用程序 2 中,我有一个函数 foo,即行

Class.forName(<CLASS NAME>)

我想将类 bar 的类路径从应用程序 1 传递到应用程序 2 中的 foo 函数,以便我可以在 foo 函数中使用 bar 类的实例。但是,当我尝试这个时,我得到一个 ClassNotFoundException。我已经将此问题与调用“Class.forName”的代码使用引导类加载器的事实隔离开来,而 bar 类是使用其他一些类加载器加载的。我无法更改应用程序 2 中的代码,但我可以更改应用程序 1 中的代码 - 有没有一种方法可以以某种方式指定应该使用引导类加载器加载栏,或者以其他方式使其可被应用程序 2 发现在 foo 函数中?

【问题讨论】:

    标签: java scala sbt classloader


    【解决方案1】:

    如果我理解正确,您的设置如下:

    • app1(= 应用程序)

      • Bar.class
    • app2(= 库)

      • 想要使用 Bar.class 的具有函数 foo(...) 的类

    它们都是由不同的类加载器加载的,您想在app2foo 函数中查找Bar.class

    app2 设置是否对app1 有任何类路径依赖,因此app1 的类实际上可以由app2 加载?如果不是,那么您能够使用该类的机会很小。

    让我们回顾一下这里发生的事情。类加载器通常在委托模型上工作。尊重委托模型的类加载器将首先检查他们的本地缓存该类之前是否已经被该类加载器加载,如果没有,则询问他们的父母他们是否知道该类的定义。只有在定义类的实际字节由启动查找链的类加载器加载之前没有任何祖先知道该类。为了加载该类定义的字节,类加载器通常会检查其类路径,如果有任何提到的包含该定义的 JAR 存档或目录。如果存在多个提供类定义的源,则将加载找到的第一个源,如果同一类的多个版本在不同的档案/目录中徘徊,则可能会导致shadowing issues

    因此,如果这两个应用程序(或应用程序和库)的类路径设置为无法访问对方的 Java 存档 (JAR),他们甚至无法加载和查找其他应用程序的类定义。可能最好的建议是将公共类提取到自己的库中,即commonsbase,并将这个库添加到两个应用程序的类路径中,以便相应应用程序/库的应用程序加载器能够加载共享类的类定义。

    但是请注意,相同的类定义,即类Bar,由两个不同的类加载器加载将导致 CL1.Bar != CL2.Bar,因为加载该类定义的类加载器是该类的一部分,这意味着该事件虽然两个类加载器中类的字节表示是相同的,但您获得的实际Class&lt;Bar&gt; 对象是不同的。如果您考虑返回在foo(...) 中加载的类定义并将其在app1 中与一些本地Bar.class 进行比较,并想知道为什么barObject.getClass() instanceof Bar(或类似的)检查失败,假设这很快就会出现问题,假设barObjectapp2.foo()Barapp1.Bar.class 返回的产品。

    关于使用引导类加载器的Class.forName(...)Class 实际上存在两个forName(...) 方法。一个只使用完全限定的类名来返回类对象,它使用实际加载相应类的类加载器,这几乎总是boostrap类加载器。第二个签名如下: Class&lt;?&gt; forName(String className, boolean initialize, Classloader loader) 您可以在其中指定用于查找和加载类定义的类加载器。因此,只要您至少有一个由其他类加载器加载的类的引用,您就可以通过otherAppObject.getClass().getClassLoader() 获得对该类加载器的引用。

    进一步注意,如果您的应用程序使用 Java 的模块功能,您可能需要打开模块,以便类加载器实际上能够加载这些类的定义。

    ...有没有一种方法可以让我以某种方式指定 bar 应该使用引导类加载器加载,或者以其他方式使其在 foo 函数中可供应用 2 发现?

    我不知道sbt TBH,但通常将app1 的JAR 归档的位置添加到app2 的类路径应该足以让app2 的类加载器加载、定义和返回app1 中使用的类的类定义。如前所述,将两者使用的公共类提取到添加到两个应用程序类路径的库中可能是推荐的方法。

    【讨论】:

      猜你喜欢
      • 2020-12-16
      • 1970-01-01
      • 1970-01-01
      • 2016-07-04
      • 2016-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多