Как заменить классы в запущенном приложении в 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 способа:
- Чтобы перезаписать загрузчик классов, который вы используете, сначала используя существующий загрузчик классов для начальной загрузки вашего приложения и, в частности, для класса, который необходимо динамически обновлять, вы должны использовать перезаписанный загрузчик классов.
- Использовать 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
А как насчет шаблона стратегии? Это может быть лучшим решением для вашей проблемы вместо использования загрузчиков классов.