【问题标题】:Is it possible to have the System ClassLoader load .class files specified at run time?是否可以让 System ClassLoader 在运行时加载指定的 .class 文件?
【发布时间】:2010-12-24 20:09:39
【问题描述】:

我正在为作业编写一个静态分析工具,它使用 ASM 库分析​​ Java 字节码。我们使用的 ASM 的其中一个部分要求(或至少似乎要求)从 ClassLoader 加载类。

我们希望该工具能够分析 .class 文件,而不需要它们位于类路径中。我们已经在运行时从指定目录加载 .classes 并使用 InputStream 读取它们。在大多数情况下,这对于 ASM 来说是可以接受的。有一些类,例如SimpleVerifier,会尝试加载这些类。

在这种情况下,是否可以注册要加载的 .class 文件,以便调用 Class.forName() 来加载它们?或者有没有一种简单的方法来扩展 ClassLoader 以允许这样做?


编辑:URLClassLoader 上的信息很有用。不幸的是,在这种情况下,对一个实例使用Thread.currentThread().setContextClassLoader() 不起作用。我调用的库代码使用了一个加载器,它使用getClass().getClassLoader() 在实例初始化时检索到。

当我设置 URLClassLoader 时,该类尚未初始化,所以我猜想 contextClassLoader 没有加载该类。

我是否正确理解了这些回答?是否可以使用 URLClassLoader 加载第 3 方类?

【问题讨论】:

  • 我认为 ASM 不需要。
  • 为什么你不能创建一个新的类加载器并指向 ASM?
  • @Tom Hawtin:在大多数情况下,可以使用ClassReader 读取类,该ClassReader 接受输入流。但是,SimpleVerifier 确实尝试加载该类,我们正在使用它来分析某些点的堆栈。如果有替代使用 SimpleVerifierAnalyzer 在字段分配点检查堆栈的替代方法,则相关信息将是有效答案。
  • @bmargulies,如果可能的话,而且很容易做到,这就是我想要的那种信息。告诉我该怎么做也是一个有效的答案。
  • @Grundlefleck 您找到适合您的案例的有效解决方案了吗?

标签: java classpath classloader bytecode java-bytecode-asm


【解决方案1】:

差不多了。

如果您在某处编译了类,则可以使用URLClassLoader 加载它们。然后可以将此 ClassLoader 设置为当前线程的 ClassLoader:Thread.setContextClassLoader(ClassLoader)

用户可以获取当前线程上下文类加载器并使用它来访问类定义。

【讨论】:

  • @Kevin 很好的提示,谢谢。但我没能使它适用于我的情况。特别是:我的项目中有一个MyClass,这取决于${java.home}/lib/deploy.jar 中的类。当我尝试通过URLClassLoader 加载这个jar,然后尝试在MyClass 中调用一个方法时,我得到java.lang.ClassNotFoundException: com.sun.deploy.services.ServiceManager。从这里 (bugs.sun.com/bugdatabase/view_bug.do?bug_id=6667564) 我看到我必须将 deploy.jar 添加到 bootclasspath(添加到类路径没有帮助),但我不想这样做并动态解决依赖关系。有什么办法吗?
  • 你能把你的自定义类加载器的父类设置为引导类加载器吗?
  • 我想,我可以。但我想避免特定于 VM 的参数,例如 -Xboothclasspath/a:,它将我的实现绑定到 Sun VM。不管怎样,给我一个提示如何制作这个。如果有纯粹的 Java 代码解决方案,现在让我来。如果您能解释一下,deploy.jar 中的具体内容是什么,我无法使用URLClassLoader 加载它,非常欢迎您。
【解决方案2】:

首先,ASM 可以不使用 ClassLoader 来获取有关类的信息。

ASM 框架中有几个地方默认加载类,但所有这些地方都可以在您自己的子类中覆盖。从我的脑海中浮出水面:

  • ClassWriter.getCommonSuperClass() 方法仅在使用 ClassWriter.COMPUTE_FRAMES 标志时调用,并且可以被覆盖以不使用 ClassLoader 来获取有关类的信息。您可以在 ClassWriterComputeFramesTest 中找到一个引入 ClassInfo 抽象的示例
  • SimpleVerifier.isAssignableFrom() 使用类似的 SimpleVerifier.getClass() 方法,您可以覆盖后者并使用 ClassInfo 抽象来查找公共超类型。如果我没记错的话,AspectWerkz 项目在其类型模式匹配代码中实现了类似的东西。另请注意,还有SimpleVerifier.setClassLoader() 方法,如果您仍想加载自己的类,可以使用该方法。

附带说明,在 Sun 的 JVM 上,加载的类会到达 PermGen 区域并且无法卸载,因此如果可以避免这种情况,仅出于静态代码分析的目的加载类不是一个好主意,尤其是如果工具将被集成到一个长期存在的进程中,例如 IDE。

【讨论】:

  • 只有 SimpleVerifier 的受保护的getClass() 方法对我造成了问题,因为它使用了getClass().getClassLoader(),但是当我问这个问题时,我确实错过了继承 SimpleVerifier 的明显答案。子类化以使用不同的 ClassLoader 是我使用的解决方案。这是我第一次使用 ASM,我对此非常满意。
  • 如果您正在使用静态分析工具,您可能不想加载类。我更新了我的答案以澄清 SimpleVerifier.getClass() 方法仅由 SimpleVerifier.isAssignableFrom() 使用,您可以在不加载类的情况下重新实现它并使其性能更高,特别是对于静态分析的需求。
  • @EugeneKuleshov - 作为 ASM 的开发人员之一,我想知道您是否可以帮助我解决以下问题:我有一个 java 代理,它使用 ASM 修改类并在客户端应用程序上使用。我已经看到第三方类加载器(PowerMock)无法使用 ClassDefNotFound 运行的场景,因为它找不到我的类之一。有没有办法让我使用 ASM 向它介绍我的课程(PowerMock),这样它就不会失败?我会感谢你的帮助。谢谢,
  • @nadavy 你为什么不问一个新问题让人们回答呢?无论是在 stackoverflow 上还是在 PowerMock 自己的论坛上,或者不在。
  • @nadavy 没有打扰我。您需要给 stackoverflow 成员一个机会来回答您的问题并赢得声誉。这在 cmets 中是做不到的。因此,请在本网站上提出新问题或直接询问 powermock(或您正在使用的任何其他工具)的开发人员。
【解决方案3】:

据我所知,您不能在运行时扩展 System 类加载器,但您可以使用 URLClassLoader 从任意位置(jar 或目录)动态加载类。

【讨论】:

  • 这不是一个好主意,但实际上可以将类添加到系统类加载器中。
【解决方案4】:

您可以尝试在您的应用程序启动时设置一个“启动器”,它创建一个URLClassLoader,将类路径上的位置和您自己的.class 位置传递给它,然后从该类加载器启动应用程序。

SimpleVerifierURLClassLoader 加载时,它还可以从额外的位置加载类。

【讨论】:

    【解决方案5】:

    是的,你可以使用URLClassLoader

    我有一个测试,我在运行时加载类。这个类不在类路径中(甚至在运行测试时也不存在),后来它被加载并且工作得很好。

    这是代码。

    void testHello() throws MalformedURLException, ClassNotFoundException {
        URL[] url = {
                new URL("file:/home/oreyes/testwork/")
        };
    
        try {
            new URLClassLoader(url).loadClass("Hello");
            throw new AssertionError("Should've thrown ClassNotFoundException");
        } catch ( ClassNotFoundException cnfe ){}
    
    
        c.process();// create the .class file 
    
        new URLClassLoader(url).loadClass("Hello");
    
        // it works!!
    }
    

    取自question

    【讨论】:

      【解决方案6】:

      我创建了自己的 ClassLoader,它非常简单。

       /**
       * Used to hold the bytecode for the class to be loaded.
       */
      private final static ThreadLocal<byte[]> BYTE_CODE = new ThreadLocal<byte[]>();
      
      @Override
      protected Class<?> findClass(final String name) throws ClassNotFoundException {
          final byte[] bytes = BYTE_CODE.get();
          if (null == bytes) {
              throw new ClassNotFoundException(name);
          }
          return this.defineClass(null, bytes, 0, bytes.length);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-28
        • 1970-01-01
        • 2012-04-06
        • 1970-01-01
        • 1970-01-01
        • 2010-09-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多