6

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

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

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


编辑:关于的信息URLClassLoader很有用。不幸的是,Thread.currentThread().setContextClassLoader()在这种情况下使用该实例不起作用。我调用的库代码使用它在实例初始化时使用getClass().getClassLoader().

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

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

4

6 回答 6

5

首先,ASM 的使用方式是它不会使用 ClassLoader 来获取有关类的信息。

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

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

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

于 2009-12-16T19:04:09.113 回答
5

几乎。

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

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

于 2009-12-09T14:03:52.980 回答
2

是的,您可以使用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!!
}

取自这个问题

于 2009-12-09T14:16:58.513 回答
2

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

于 2009-12-09T14:01:22.987 回答
2

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

当它SimpleVerifier被加载时,URLClassLoader它也将能够从额外的位置加载类。

于 2009-12-09T14:12:42.390 回答
1

我创建了自己的 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);
}
于 2010-02-10T08:51:11.723 回答