Как заставить Java перезагрузить класс при создании экземпляра? - PullRequest
18 голосов
/ 19 октября 2010

Справочная информация:
У меня есть класс MainTest, который имеет много кнопок, каждая из которых создает экземпляр класса, который я кодирую / тестирую.Я хочу, чтобы цикл код / ​​тестирование для этих классов был быстрым, и я вижу результат моих изменений быстро, несколько раз в минуту.Стабильный MainTest занимает около 20 секунд для загрузки, что не было бы проблемой, если бы мне не нужно было перезагружать его для каждого изменения в классах, которые он создает.Я хочу загрузить MainTest один раз, и когда он создает экземпляр другого класса, назовем его ChildTest, много раз (после события кнопки), он должен перезагрузить последнюю версию ChildTest.

Короче вопрос:
Как вы скажете команде java 'new' перезагрузить класс с диска, а не из кэша jvm?


Я пробовал Class.ForName, но это не имело значения.
Я также пытался использовать пользовательский загрузчик классов (скопированный из открытого исходного кода), но безрезультатно.

Ответы [ 6 ]

27 голосов
/ 19 октября 2010

Нет надежды на «перегрузку» оператора new, но вы, безусловно, могли бы написать собственный загрузчик классов, который просто перезагружает байт-код каждый раз, когда вы просите его загрузить класс. Никакие готовые загрузчики классов не будут делать то, что вы ищете, потому что все они предполагают, что определение класса не изменится в течение жизни JVM.

Но вот как ты это делаешь. Создайте загрузчик классов с именем, скажем, Reloader, который переопределяет методы loadClass и findClass, чтобы они просто перезагружали файлы классов с диска при каждом вызове (вместо «кэширования» их для последующего использования). Затем вам просто нужно вызвать new Reloader().loadClass("foo.bar.MyClassName") каждый раз, когда вы подозреваете, что определение класса изменилось (например, как часть методов жизненного цикла вашей инфраструктуры тестирования).

Эта статья содержит некоторые детали, но упускает некоторые важные моменты, особенно об использовании новых экземпляров загрузчика классов для последующих перезагрузок и делегировании загрузчику классов по умолчанию, когда это необходимо. Вот простой рабочий пример, который многократно загружает класс MyClass и предполагает, что его файл класса существует в относительной директории "./bin":

public class Reloader extends ClassLoader {
    public static void main(String[] args) throws Exception {
        do {
            Object foo = new Reloader().loadClass("MyFoo").newInstance();
            System.out.println("LOADED: " + foo); // Overload MyFoo#toString() for effect
            System.out.println("Press <ENTER> when MyFoo.class has changed");
            System.in.read();
        } while (true);
    }

    @Override
    public Class<?> loadClass(String s) {
        return findClass(s);
    }

    @Override
    public Class<?> findClass(String s) {
        try {
            byte[] bytes = loadClassData(s);
            return defineClass(s, bytes, 0, bytes.length);
        } catch (IOException ioe) {
            try {
                return super.loadClass(s);
            } catch (ClassNotFoundException ignore) { }
            ioe.printStackTrace(System.out);
            return null;
        }
    }

    private byte[] loadClassData(String className) throws IOException {
        File f = new File("bin/" + className.replaceAll("\\.", "/") + ".class");
        int size = (int) f.length();
        byte buff[] = new byte[size];
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        dis.readFully(buff);
        dis.close();
        return buff;
    }
}

При каждом вызове блока do / while в методе main создается новый Reloader, который загружает класс с диска и возвращает его вызывающей программе. Таким образом, если вы перезаписываете файл bin/MyClass.class, чтобы он содержал новую реализацию с другим, перегруженным методом toString, то вы должны видеть новую реализацию каждый раз.

1 голос
/ 20 октября 2010

Вы можете попробовать Java Rebel . Это загрузчик классов «только для разработки», предназначенный для выполнения именно того, что вам нужно, но, возможно, он может быть дорогим для ваших нужд. В вашем случае, решения Питера Лори может быть достаточно.

1 голос
/ 20 октября 2010

Я нашел статью именно об этой проблеме.
http://www.zeroturnaround.com/blog/reloading-objects-classes-classloaders/

НО, ответ maerics тоже выглядит хорошо. попробую позже.

1 голос
/ 19 октября 2010

Звучит немного страшно, но это должно помочь.

http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html

ClassLoader может динамически загружать классы во время выполнения, я бы прочитал API, чтобы определить, перезаписывает ли его загрузка предыдущую версию.

1 голос
/ 19 октября 2010

Похоже, вы не хотите использовать горячее развертывание, когда его можно использовать с отладчиком. Когда вы отлаживаете проблему и перекомпилируете некоторые из ее классов, вы можете получить возможность перезагрузить измененные классы.

РЕДАКТИРОВАТЬ: Помимо использования API отладки, вы можете использовать Инструментарий. http://download -llnw.oracle.com / JavaSE / 6 / документы / API / Java / языки / инструмент / пакет-summary.html

Однако, поскольку использование отладчика на сегодняшний день является самым простым способом сделать это, если это не сработает, вы, вероятно, столкнетесь с теми же проблемами.

Звучит так, как будто вам нужно протестировать небольшие части работы, поэтому для запуска некоторого подмножества вашего приложения требуется менее секунды.

Или вы могли бы загружать ваше приложение быстрее, предоставляя возможность дампа и повторной загрузки памяти. Таким образом, вы можете запустить свое приложение из моментального снимка (возможно, сразу)

0 голосов
/ 19 октября 2010

Я не уверен, как это сделать в коде, но в NetBeans есть кнопка «Применить изменения кода», которая сделает это.

Однако, он может только изменить реализацию класса, он не может изменить свою подпись. Это означает, что вы не можете добавлять, удалять или изменять переменные или методы экземпляра. Это связано с конструкцией JVM, которая не позволяет изменять их после однократной загрузки класса.

Поскольку NetBeans может это делать, должен быть способ, но я не знаю, как это сделать вручную.

Если вы работаете в среде IDE, которая поддерживает эту функцию, вы можете просто нажимать кнопку каждый раз, когда изменяете класс. Затем он перекомпилирует этот класс и перезагрузит его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...