是否可以在运行时指定System ClassLoader加载.class文件?

我正在编写一个用于赋值的静态分析工具,它使用ASM库分析Java字节码。 我们使用的ASM的一个部分需要(或者至少看起来需要)从ClassLoader加载类。

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

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


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

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

我是否正确理解了答案? 是否可以使用URLClassLoader加载第三方类?

几乎。

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

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

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

ASM框架中有几个地方默认加载类,但所有这些地方都可以在您自己的子类中重写。 脱离我的头脑:

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

另外,在Sun的JVM上,加载的类到达PermGen区域并且无法卸载,因此如果可以避免这种情况,仅为静态代码分析加载类不是一个好主意,特别是如果工具是集成到一个长期过程中,例如IDE。

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

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

URLClassLoader加载SimpleVerifier时,它也可以从额外的位置加载类。

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

取自这个问题 。

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

  /** * Used to hold the bytecode for the class to be loaded. */ private final static ThreadLocal BYTE_CODE = new ThreadLocal(); @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); }