Как заменить классы в запущенном приложении в Java?

Скажем, у меня есть класс с именем NameGenerator, Я могу использовать это для генерации имен в соответствии с заданной логикой. Тогда я пишу TestNameGeneration Класс с методом, который запрашивает письмо от пользователя и генерирует имя в соответствии. Теперь я хочу изменить логику в NameGeneration Класс и применить это конкретное изменение без остановки приложения.

Я сделал это, чтобы узнать больше о загрузчиках классов, и кто-то может объяснить, какие ключевые понятия мне нужно изучить, чтобы сделать что-то подобное или разместить какие-либо ссылки?

5 ответов

Решение

Вот рабочий тест. Каждые 5 секунд Test.main() перезагружает test.Test1.class из файловой системы и вызывает Test1.hello()

package test;

public class Test1 {
    public void hello() {
        System.out.println("Hello !");
    }
}

public class Test {

    static class TestClassLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            if (name.equals("test.Test1")) {
                try {
                    InputStream is = Test.class.getClassLoader().getResourceAsStream("test/Test1.class");
                    byte[] buf = new byte[10000];
                    int len = is.read(buf);
                    return defineClass(name, buf, 0, len);
                } catch (IOException e) {
                    throw new ClassNotFoundException("", e);
                }
            }
            return getParent().loadClass(name);
        }
    }

    public static void main(String[] args) throws Exception {
        for (;;) {
            Class cls = new TestClassLoader().loadClass("test.Test1");
            Object obj = cls.newInstance();
            cls.getMethod("hello").invoke(obj);
            Thread.sleep(5000);
        }
    }
}

Запустить его. Затем измените и перекомпилируйте Test1

System.out.println("Hello !!!");

пока тест запущен. Вы увидите, что выход Test1.hello изменен

...
Hello !
Hello !
Hello !!!
Hello !!!

Так Tomcat перезагружает веб-приложения. Он имеет отдельный ClassLoader для каждого веб-приложения и загружает новую версию в новый ClassLoader. Старый GCed, как и любой объект Java, а также старые классы.

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

Есть 2 способа:

  1. Чтобы перезаписать загрузчик классов, который вы используете, сначала используя существующий загрузчик классов для начальной загрузки вашего приложения и, в частности, для класса, который необходимо динамически обновлять, вы должны использовать перезаписанный загрузчик классов.
  2. Использовать OSGi Framework. В зависимости от масштаба вашего приложения, OSGi Framework может не быть хорошим выбором, так как требует от вас соблюдения соглашения о кодировании.

Надеюсь, поможет

Напишите свой собственный загрузчик классов:

Я имел успех с этим в качестве основы. Вы также должны посмотреть на этот учебник.

Это действительно просто, вы будете использовать преимущества ООП и использовать интерфейс, не нужно управлять загрузчиком классов. Вы можете внедрить реализацию с помощью установщика:

Создайте интерфейс с именем NameGeneration. Создайте n-реализацию NameGenerationImpl1, например NameGenerationimpl2. В вашем клиентском классе определите переменную:

NameGeneration nameGeneration ;

и сеттер:

 public void setNameGeneration(NameGeneration nameGeneration) {
 this.nameGeneration = nameGeneration ;
}

nameGeneration будет генерировать то, что вы хотите.

Когда вы меняете алгоритм, вы меняете реализацию, например:

setNameGeneration(new NameGenerationImpl1()) ;

или же

setNameGeneration(new NameGenerationImpl2()) ;

Вы можете написать свой собственный загрузчик классов. Если когда-либо произойдет изменение в файле класса или ресурсе /jar, содержащем файл класса (проверьте метку времени), уничтожьте предыдущий экземпляр загрузчика классов и создайте новый экземпляр загрузчика классов, который, в свою очередь, загрузит новый файл класса.

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

java -jar DCEVM-8u181-installer.jar

И вы должны добавить ниже аргументы VM к вашей конфигурации команды (или) Run

-XXaltjvm = dcevm -javaagent: Your_directory_location / hotswap-agent-1.3.0.jar.jar = autoHotswap = true

Ссылка: https://dzone.com/articles/hot-swap-java-bytecode-on-runtime

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

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