我正在开发一个项目,它涉及读取给定JAR文件中的所有类,修改这些类的部分(在字节码级别,使用ASM),并创建一个ClassLoader
这些修改过的类。目前,在修改类之后,我创建了ClassLoader
类的自定义实例,使defineMethod(String, byte[])
可见。
以下是自定义ClassLoader
的代码(它是嵌套类):
private static class ExposingClassLoader extends ClassLoader {
private ExposingClassLoader(JarFile jar) throws MalformedURLException {
super(new URLClassLoader(new URL[] { new URL("jar:" + new File(jar.getName()).toURI().toURL() + "!/") }));
}
private void defineClass(String name, byte[] data) {
defineClass(name, data, 0, data.length);
}
}
您可能已经注意到我将父类加载器设为URLClassLoader
。当我构造自定义类加载器时,我使URLClassLoader
引用包含未修改的类文件的JAR。在修改了一些类文件之后,我遍历它们并为每个文件调用defineMethod(String, byte[])
。一些修改后的类相互依赖,即A类可能是B类的超类,或者C类可能实现D类,或者E类可能包含对F类的引用,等等
对于我的测试,我使用两个类。第一个名为fn
,第二个名为fi
,fi
名为fn
,包含多个fi
字段。这两个类都存在于用于构造ClassLoader
的JAR文件中,唯一的区别是它们未经修改。出于某种原因,如果我定义类fn
它工作正常(它不应该?JAR文件中已存在类fn
)。但是,如果我尝试定义fi
,再次多次引用fn
,我会遇到以下异常:
Exception in thread "main" java.lang.LinkageError: loader (instance of Injector$ExposingClassLoader): attempted duplicate class definition for name: "fn"
在线:
defineClass(name, data, 0, data.length);
我不知道如何处理这件事。最简单的方法是“卸载”JAR中包含的类,但只包括我修改过的那些类。这样,当我定义修改后的类时,它们将不会被加载到ClassLoader
中。但是,我环顾四周,我还没有找到一个干净的方法来做到这一点。有人说我需要单独的(多个)类加载器,这对我的项目不起作用,除非有一种方法可能将这些多个类加载器组合成一个,就像包装器一样。
如何重新定义ClassLoader
中的课程?
答案 0 :(得分:2)
我找到了解决问题的方法。基本上,我应该覆盖defineClass
而不是覆盖findClass
的行为。这是有效的代码。
public class ModifiableClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions;
public ModifiableClassLoader(JarFile jar, Map<String, byte[]> definitions) throws MalformedURLException {
super(new URLClassLoader(new URL[] { new URL("jar:" + new File(jar.getName()).toURI().toURL() + "!/") }));
this.definitions = definitions;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = definitions.remove(name);
if (classBytes != null) {
return defineClass(name, classBytes, 0, classBytes.length);
}
return super.findClass(name);
}
}
它根据另一个我现在找不到的Stack Overflow问题的答案写了它。我以前一定忽略了它。