自定义 Java 类加载器未用于加载依赖项?

2023-12-26

我一直在尝试设置一个自定义类加载器来拦截类以打印出哪些类正在加载到应用程序中。类加载器看起来像这样

public class MyClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        System.out.println("Loading: " + name);
        return super.loadClass(name);
    }
}     

它只是输出它加载的所有类的名称。但是,当我尝试运行一些代码时,

import org.python.util.PythonInterpreter;
public class Scripts {
    public String main(){

        PythonInterpreter p = new PythonInterpreter();
        p.exec("print 'Python ' + open('.gitignore').read()");

        return "Success! Nothing broke";
    }
}

via

MyClassLoader bcl = new MyClassLoader();
Class c = bcl.loadClass("Scripts");

Method m = c.getMethod("main");
String result = (String) m.invoke(c.getConstructor().newInstance());

它打印出来

Loading: Scripts
Loading: java.lang.Object
Loading: java.lang.String
Loading: org.python.util.PythonInterpreter
Python build/
.idea/*
*.iml
RESULT: Success! Nothing broke

这看起来很奇怪。org.python.util.PythonInterpreter不是一个简单的类,它依赖于一大堆其他类org.python.util包裹。这些类显然正在加载,对于execpython 代码能够执行一些操作并读取我的文件。但由于某种原因,这些类没有被加载的类加载器加载PythonInterpreter.

这是为什么?我的印象是类加载器用于加载类C将用于加载所需的所有其他类C,但这显然不会发生在这里。这个假设是错误的吗?如果是,我如何设置它,以便所有传递依赖项C由我的类加载器加载?

EDIT:

使用一些实验URLClassLoader,这是建议的。我修改了委托loadClass():

try{
    byte[] output = IOUtils.toByteArray(this.getResourceAsStream(name));
    return instrument(defineClass(name, output, 0, output.length));
}catch(Exception e){
    return instrument(super.loadClass(name));
}

以及将 MyClassLoader 子类化为 URLClassLoader 而不是普通的 ClassLoader,通过以下方式获取 URL:

super(((URLClassLoader)ClassLoader.getSystemClassLoader()).getURLs());

但这似乎不是正确的事情。尤其,getResourceAsStream()对于我请求的所有类,甚至是像 Jython 库这样的非系统类,都会向我抛出空值。


类加载基础知识

有两个主要的地方可以扩展类加载器来改变类的加载方式:

  • findClass(String name) - 当您想要时可以覆盖此方法 找一个平时家长先委派的班级。
  • loadClass(String name, boolean resolve) - 当您想要更改时覆盖此方法 类加载委托的完成方式。

然而,类只能来自java.lang.ClassLoader 提供的最终defineClass(...) 方法。由于您希望捕获所有已加载的类,因此我们需要重写 loadClass( String, boolean ) 并在其中的某个位置调用 DefineClass(...) 。

NOTE:在defineClass(...) 方法内部,有一个到JVM 本机端的JNI 绑定。在该代码内部,会检查 java.* 包中的类。它只会让系统类加载器加载这些类。这可以防止您弄乱 Java 本身的内部结构。

子级第一类加载器示例

这是您尝试创建的类加载器的一个非常简单的实现。它假设您需要的所有类都可供父类加载器使用,因此它仅使用父类作为类字节的源。为了简洁起见,此实现使用 Apache Commons IO,但它可以轻松删除。

import java.io.IOException;
import java.io.InputStream;

import static org.apache.commons.io.IOUtils.toByteArray;
import static org.apache.commons.io.IOUtils.closeQuietly;
...
public class MyClassLoader
  extends ClassLoader {
  MyClassLoaderListener listener;

  MyClassLoader(ClassLoader parent, MyClassLoaderListener listener) {
    super(parent);
    this.listener = listener;
  }

  @Override
  protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException {
    // respect the java.* packages.
    if( name.startsWith("java.")) {
      return super.loadClass(name, resolve);
    }
    else {
      // see if we have already loaded the class.
      Class<?> c = findLoadedClass(name);
      if( c != null ) return c;

      // the class is not loaded yet.  Since the parent class loader has all of the
      // definitions that we need, we can use it as our source for classes.
      InputStream in = null;
      try {
        // get the input stream, throwing ClassNotFound if there is no resource.
        in = getParent().getResourceAsStream(name.replaceAll("\\.", "/")+".class");
        if( in == null ) throw new ClassNotFoundException("Could not find "+name);

        // read all of the bytes and define the class.
        byte[] cBytes = toByteArray(in);
        c = defineClass(name, cBytes, 0, cBytes.length);
        if( resolve ) resolveClass(c);
        if( listener != null ) listener.classLoaded(c);
        return c;
      } catch (IOException e) {
        throw new ClassNotFoundException("Could not load "+name, e);
      }
      finally {
        closeQuietly(in);
      }
    }
  }
}

这是一个简单的监听器接口,用于监视类加载。

public interface MyClassLoaderListener {
  public void classLoaded( Class<?> c );
}

然后,您可以创建 MyClassLoader 的新实例,以当前类加载器作为父级,并在加载类时监视类。

MyClassLoader classLoader = new MyClassLoader(this.getClass().getClassLoader(), new MyClassLoaderListener() {
  public void classLoaded(Class<?> c) {
    System.out.println(c.getName());
  }
});
classLoader.loadClass(...);

这适用于最一般的情况,并且允许您在加载类时收到通知。但是,如果这些类中的任何一个创建了自己的子一级类加载器,那么它们就可以绕过此处添加的通知代码。

更高级的类加载

要真正捕获正在加载的类,即使子类加载器覆盖了 loadClass(String, boolean),您也必须在正在加载的类和它们可能对 ClassLoader.defineClass(...) 进行的任何调用之间插入代码。为此,您必须开始使用类似的工具进行字节码重写ASM http://asm.ow2.org。我有一个项目叫Chlorine https://github.com/ctrimble/chlorineGitHub 上使用此方法重写 java.net.URL 构造函数调用。如果您对在加载时弄乱类感到好奇,我会检查该项目。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义 Java 类加载器未用于加载依赖项? 的相关文章

随机推荐

  • PHP 中出现意外的 T_ELSE 错误 [已关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我正在研究一本 php 书中的示例 并且使用此代码在第 8 行收到错误
  • 在哪些移动平台上,“推送令牌”不是永久的?

    我们都知道 为了在任何移动平台上使用推送通知功能 我们需要一个称为 推送令牌 的唯一标识符 开发人员需要以某种方式将该 令牌 保存到他的服务器上 以便能够向用户发送通知 然而 在哪些移动平台上 这个 令牌 将保持永久 并且在哪些移动平台上可
  • 如何在 Scala 中找到列表中最大值的索引?

    对于 Scala List Int 我可以调用方法 max 来查找最大元素值 如何找到最大元素的索引 这就是我现在正在做的事情 val max list max val index list indexOf max 一种方法是使用索引压缩列
  • 如何使用 Flexbox 将菜单居中[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个二行菜单 我想使用 Flexbox 将其居中 我是 Flexbox 新手 有几个问题 当我在 DreamWeaver CS6
  • 我可以将 Django 模型中的至少一个字段作为要求吗?

    假设我有一个 Person 模型 class Person models Model name models CharField max length 50 email models EmailField telephone models
  • 有没有可以重新格式化 cmake 文件的实用程序

    我有相对复杂的 cmake 文件 我想知道是否有任何实用程序可以重新格式化 cmakeList txt 并美化它以便于阅读 一些简单的技巧就可以完成这项工作 例如缩进 if else endif 部分等等 有这样的工具吗 我使用notepa
  • R中的叠加直方图

    我想根据位置绘制长度直方图 我试图覆盖直方图 其中一个位置的数据是一种颜色 而另一个位置的数据是不同的颜色 这是我到目前为止仅绘制直方图的 R 代码 fasta lt read csv fastadata csv header T nort
  • 逐块迭代加载图像,其中块部分重叠

    尝试处理大型卫星图像 10GB 为了有效地处理图像块 block tile 在每次迭代中被加载到内存中 其示例代码如下 def process image src img dst img band id 1 with rasterio op
  • Swift:通过身份查找数组

    swift 中的 find 函数仅支持查找项相等 我想按身份查找项目 例如 鉴于此设置 class A let first A let last A let absent A let array first last 我希望能够做类似的事情
  • 如何在 SQLite 数据库中存储 JSON 对象

    如何在 SQLite 数据库中存储 JSON 对象 正确的方法是什么 其中一处是 blob 类型列 如果我可以将 JSON 对象转换为字节数组并使用 Fileoutputstream 另一个想法是将文本列存储为字符串 import org
  • dpkg:处理存档时出错(安装 Nodejs 时)

    安装nodejs时出现这个错误 https i stack imgur com JMRiT jpg https i stack imgur com JMRiT jpg https i stack imgur com JMRiT jpg 我正
  • 通过 JupyterLab 的链接在当前工作区中打开笔记本

    我需要使用 降价 链接从另一个笔记本打开一个笔记本 但我不想在另一个浏览器选项卡中打开链接的笔记本 相反 它应该在当前的 JupyterLab 工作区 会话中打开 I tried http localhost 8888 lab tree p
  • 精确匹配字符串

    仅当文本完全匹配时才匹配的正则表达式 如果重要的话 在 JavaScript 中 是什么 也就是说 字符串的另一端不应有多余的字符 例如 如果我想匹配abc then 1abc1 1abc and abc1不会匹配 使用开始和结束分隔符 a
  • 无法使 C++ Boost 指针序列化工作

    此问题与重复列出的问题不同 Boost C 序列化 char https stackoverflow com questions 5094750 boost c serializing a char我没有使用shared ptr 并且我可以
  • 在 bash 脚本中使用 ssh 密钥

    我已经设置了从服务器 A 到服务器 B 的 ssh 密钥 并且无需密码即可登录服务器 B 我正在尝试在 bash 脚本中设置反向 ssh 隧道 如果我这样做的话 从命令行 ssh N R 1234 localhost 22 email pr
  • 如何修复npm审计报告

    当我运行 npmaudit 命令时 npm 审计安全报告 Manual Review Some vulnerabilities require your attention to resolve Visit https go npm me
  • .net 文化特定的 12/24 小时格式

    有没有办法保留文化特定的日期时间格式 但强制 12 24 小时渲染 我知道我可以使用实际的日期 时间格式字符串做很多事情 例如HH mm ss and hh mm ss但我想尊重当前的用户文化格式 即mm dd yyyy or yyyy m
  • 图像渲染测试

    我正在对自定义浏览器进行基准测试 并希望对相同文件大小的不同类型图像 gif jpg png 的渲染速度进行基准测试 以了解该浏览器渲染哪种图像格式最快 我的过程只是为每种类型的图像创建一个简单的单独 HTML 页面 并在渲染之前和之后使用
  • 将 EJB 转换为 JSON Web 服务

    我已将 EJB 3 1 转变为 Web 服务 现在我需要创建 JSON Web 服务来与 JavaScript 组件进行通信 JavaScri t 组件将使用 JSON 版本的接口与系统通信并获取所需的数据 由于性能原因 我不能使用 XML
  • 自定义 Java 类加载器未用于加载依赖项?

    我一直在尝试设置一个自定义类加载器来拦截类以打印出哪些类正在加载到应用程序中 类加载器看起来像这样 public class MyClassLoader extends ClassLoader Override public Class