Я хотел бы знать, как работает горячая перезагрузка/развертывание в Java и Spring Boot.
В Java любые файлы jar, которые мы передали JVM в качестве системного пути к классам (передаются в системный загрузчик классов), не могут быть изменены во время выполнения. Итак, мы не можем передать jar в JVM, нам нужно обрабатывать загрузчик классов самостоятельно (с помощью специального загрузчика классов).
Предположим, мы создаем URLClassloader (который будет обрабатывать файлы jar, которые мы хотим перезагрузить/развернуть в горячем режиме). Добавлен файл jar в наш собственный загрузчик классов (назовем его c1) при запуске приложения. (Примечание: мы можем только добавить файл jar в URLClassLoader, но не можем редактировать или обновлять). Таким образом, единственный вариант обновления файла jar — закрыть текущий экземпляр URLClassLoader (c1) и создать новый с обновленным файлом jar (назовем его c2).
Итак, нам нужно выполнять эту операцию непрерывно для каждого обновления (что не является правильным способом), и на этом пути тоже есть некоторые проблемы, один из примеров (я знаю 😅) заключается в том, что если я использую какой-либо из классы в этом jar-файле, JVM загружает этот конкретный файл класса и сохраняет его в своей памяти, чтобы в следующий раз получить класс из внутренней памяти.
Даже после закрытия пользовательского URL-адреса ClassLoader класс по-прежнему доступен. мы можем вызвать сборщик мусора явно, но здесь это нам не поможет (не гарантируется, что сборщик мусора будет вызван после system.gc()).
И я вижу, что инструменты разработки Spring Boot поддерживают горячую замену, я хочу понять, как это работает BTS и есть ли какой-либо способ добиться горячей перезагрузки/развертывания в рабочей среде на Java.
Если я упомянул что-то неправильное или неправильно понятое, извините и, пожалуйста, добавьте свой комментарий или поделитесь любым блогом или видео, чтобы понять внутреннюю реализацию горячей перезагрузки/развертывания в Java или любой другой платформе (Spring или любой другой).
Заранее спасибо.
Создан собственный загрузчик классов с использованием URLClassLoader.




Существует несколько способов перезагрузить классы приложения Java во время его работы.
Проще говоря, приложение может выгружаться с помощью ClassLoader или внешних инструментов (например, отладчиков), которые могут взаимодействовать с JVM и сообщать ей о необходимости переопределить классы.
Spring Boot devtools поддерживает автоматический перезапуск:
Приложения, использующие
spring-boot-devtools, автоматически перезапускаются при каждом изменении файлов в пути к классам. Это может быть полезной функцией при работе в IDE, поскольку она обеспечивает очень быстрый цикл обратной связи для изменений кода. По умолчанию любая запись в пути к классам, указывающая на папку, отслеживается на предмет изменений.
По сути, Spring может обнаруживать изменения в пути к классам, останавливать все, что связано со Spring, и запускать его снова. Это работает, потому что Spring имеет свою собственную логику жизненного цикла (см. также это ), а его компоненты могут отключаться, а затем воссоздаваться заново. Spring действительно делает это , как вы можете видеть в документации:
Технология перезапуска, предоставляемая Spring Boot, работает с использованием двух загрузчиков классов. Классы, которые не изменяются (например, из сторонних jar-файлов), загружаются в базовый загрузчик классов. Классы, которые вы активно разрабатываете, загружаются в перезапускаемый загрузчик классов. При перезапуске приложения загрузчик классов перезапуска удаляется и создается новый.
Как только приложение полностью остановлено, оно может закрыть ClassLoader, убедиться, что все действительно остановлено, и загрузить все снова. также можно использовать родительский ClassLoader для классов библиотеки, которые, как предполагается, не изменяются, и дочерний ClassLoader для кода приложения, который может быть перезагружен.
Если вы посмотрите на приложение, использующее Spring devtools с включенной логикой перезапуска, вы можете увидеть, что классы на самом деле загружаются с помощью RestartClassLoader , который является подклассом URLClassLoader. Вы можете проверить исходный код здесь.
Определенные части приложений Spring, такие как статические файлы , также можно заменять без перезапуска контекста.
Хотя логика перезапуска/перезагрузки может быть частью приложения, она также может предоставляться IDE или другими инструментами вне приложения. Это можно сделать с помощью API отладки или с помощью агентов/инструментов.
Например, если вы запустите Java-приложение в Eclipse в режиме отладки и измените некоторые классы, оно автоматически заменит эти классы во время выполнения . Функциональность переопределения классов обеспечивается API-интерфейсами отладки (см. также документацию JVM TI по переопределению классов).
Это не выгружает класс и не загружает новый класс, но заменяет код в загруженных методах и (обычно) не работает, если изменяются сигнатуры методов или поля.
Если мы посмотрим исходный код Eclipse JDT-debug , то увидим, что он прослушивает измененные классы, проверяет, можно ли все заменить в горячем режиме , а затем выполняет горячую замену здесь.
Если все работает, он выходит из методов, если в данный момент выполняется заменяемый код , сообщает JVM переопределить классы и, наконец, возвращается к переопределенным методам.
Помимо этого, существуют и другие инструменты, такие как JRebel , которые используют инструменты/агенты и их не нужно запускать в режиме отладки. Обратите внимание: это коммерческий инструмент, которым я никогда не пользовался, поэтому не знаю, как именно он работает. Тем не менее, я думаю, что об этом все же стоит упомянуть из-за другого подхода. Для этого также можно использовать что-то вроде JVM TI.
Что касается перезапуска IDE, проект под названием DCEVM даже позволяет перезагружать классы при изменении сигнатур. Публикации, разъясняющие, как это работает, вы можете найти здесь.
Отказ от ответственности: на момент написания этой статьи я учусь в JKU, где он изначально был разработан, но я никоим образом не участвовал в этом проекте.
Как вы также спросили
есть ли способ добиться горячей перезагрузки/развертывания в производстве на Java
Я рекомендую не использовать эти подходы в производстве, поскольку это может привести к несогласованности.
Например, при замене горячего кода на основе отладки/инструментария вы могли бы назначить что-то полю, но затем замените код в каком-то методе, который теперь предполагает другое назначение поля, или это также может привести к многократному выполнению некоторого кода. раз. Эти несоответствия хороши для разработки, но опасны в производстве. Горячая замена кода (или как ее называет ваш инструмент) — это функция разработки.
Подход Spring может быть более надежным, но он также может привести к некоторым несоответствиям, если используемая вами библиотека запускает вещи, которые не останавливаются правильно или аналогично.
Однако со многими серверами приложений или с OSGi или аналогичными решениями, как упомянуто в этом другом ответе от Dudi Boy, вы можете использовать аналогичные подходы, созданные для производства. Многие серверы приложений созданы для запуска нескольких приложений в одной JVM и позволяют останавливать, запускать и повторно развертывать некоторые из них без перезапуска всей JVM.
Если вы ничего подобного не используете, просто перезапустите приложение. Существует также несколько способов перезапуска приложения. Если время запуска является проблемой, вы можете использовать такие вещи, как AppCDS (см. это улучшение удобства использования), чтобы улучшить его.
Вы можете изменить классы внутри существующего jar, используя HotswapAgent (используйте опцию extraClasspath)+DCEVM.
Оригинальный проект DCEVM мертв. Вы можете использовать TravaOpenJDK или среду выполнения Jetbrains, в которую встроена расширенная версия DCEVM. Как уже упоминалось, я не рекомендую использовать его в производстве, если вы точно не знаете, что делаете.
Обратите внимание, что (AFAIK) DCEVM необходим только в том случае, если вы хотите изменить сигнатуры методов или поля.
Spring Dev Tools (как и большая часть Spring) имеет открытый исходный код, проверьте исходники.