Определение одного и того же класса дважды с помощью загрузчика классов



Я работаю над проектом, который включает в себя чтение всех классов в данном файле 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?

545   1  

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

    Ничего не найдено.