Определение одного и того же класса дважды с помощью загрузчика классов
Я работаю над проектом, который включает в себя чтение всех классов в данном файле 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);
}
}
Вы, возможно, заметили, что я сделал родительский класс загрузчик a URLClassLoader. Когда я создаю пользовательский загрузчик классов, я заставляю URLClassLoader ссылаться на JAR, который содержит неизмененные файлы классов. После модификации некоторых файлов класса я перебираю их и вызываю defineMethod(String, byte[]) для каждого из них. Некоторые из модифицированных классов зависят друг от друга, то есть класс A может быть суперклассом класса B, или класс C может реализовывать класс D, или класс E может содержать ссылки на класс F, и так далее.
Для моих тестов я использую два класса. То первый называется
fn, а второй - fi, а fi расширяет fn и содержит несколько полей типа fi. Оба этих класса присутствуют в файле JAR, который используется для построения ClassLoader, единственное отличие заключается в том, что они не модифицированы. По какой-то причине, если я определяю класс fn, он работает нормально (чего не должно быть? Класс fn уже существует в файле JAR). Однако, если я попытаюсь определить 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);
И я не знаю, как с этим бороться. Самый простой способ сделать это - "выгрузить" классы, которые содержатся в банке, но только те, которые я изменил. Таким образом, когда я определяю свои модифицированные классы, они уже не будут загружены в
ClassLoader. Тем не менее, я осмотрелся вокруг, и мне еще предстоит найти чистый способ сделать это. Некоторые говорят, что мне нужны отдельные (множественные) загрузчики классов, и это не будет работать для моего проект, если только не было способа объединить эти несколько загрузчиков классов в один, как обертку.Как я могу переопределить класс в ClassLoader?
1 ответ:
Я нашел решение моей проблемы. В принципе, вместо того, чтобы переопределять поведение
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); } }
Comments