所以我有一个类加载器(MyClassLoader),它在内存中维护一组“特殊”类。这些特殊类被动态编译并存储在 MyClassLoader 中的字节数组中。当 MyClassLoader 被要求提供一个类时,它首先检查它的specialClasses
字典是否包含它,然后再委托给 System 类加载器。它看起来像这样:
class MyClassLoader extends ClassLoader {
Map<String, byte[]> specialClasses;
public MyClassLoader(Map<String, byte[]> sb) {
this.specialClasses = sb;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (specialClasses.containsKey(name)) return findClass(name);
else return super.loadClass(name);
}
@Override
public Class findClass(String name) {
byte[] b = specialClasses.get(name);
return defineClass(name, b, 0, b.length);
}
}
如果我想对 执行转换(例如检测)specialClasses
,只需byte[]
在调用defineClass()
它之前修改 即可。
我还想转换 System 类加载器提供的类,但是 System 类加载器似乎没有提供任何访问byte[]
它提供的原始类的方法,而是直接给我Class
对象。
我可以使用-javaagent
加载到 JVM 中的所有类的仪器,但这会增加我不想仪器的类的开销;我只希望对 MyClassLoader 加载的类进行检测。
- 有没有办法检索
byte[]
父类加载器提供的原始类,所以我可以在定义自己的副本之前对它们进行检测? - 或者,是否有任何方法可以模拟 System 类加载器的功能,就它从哪里抓取它而言
byte[]
,以便 MyClassLoader 可以检测和定义它自己的所有 System 类(对象、字符串等)的副本?
编辑:
所以我尝试了另一种方法:
- 使用 a
-javaagent
,捕获byte[]
每个加载的类并将其存储在哈希表中,以类的名称为键。 - MyClassLoader 不是将系统类委托给它的父类加载器,而是使用类名从这个哈希表加载它们的字节码并定义它
从理论上讲,这将让 MyClassLoader 使用仪器定义自己的系统类版本。但是,它失败了
java.lang.SecurityException: Prohibited package name: java.lang
很明显,JVM 不喜欢我java.lang
自己定义类,即使它(理论上)应该来自与byte[]
引导加载的类应该来自同一个来源。继续寻找解决方案。
编辑2:
我为这个问题找到了一个(非常粗略的)解决方案,但如果有人比我更了解 Java 类加载/仪器的复杂性,可以想出一些不那么粗略的东西,那就太棒了。