Как заставить Java перезагрузить класс при создании экземпляра?

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

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

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

6 ответов

Решение

Нет надежды на "перегрузку" 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 метод, то вы должны увидеть новую реализацию каждый раз.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам