Могу ли я динамически выгружать и перезагружать (другие версии того же самого) JAR?
Я пишу серверную программу, которая используется для запуска модульных тестов API
(отображение большого количества информации и обеспечение доступа в Интернет для управления
/контролировать все это)...
Этот API известен серверу во время компиляции и предоставляется
как JAR.
Чтобы иметь возможность сравнивать результаты unit test разных версий
API (без перезапуска сервера),
Я хочу иметь возможность выгрузить "текущую" версию API,
и перезагрузить более новую (или более старую).
Я не хочу использовать URLClassLoader и вызывать каждый
метод по названию
(используя getDeclaredMethod("someMethod")
),
потому что сервер сильно зависит от API, и это будет
сложно "обернуть" каждый вызов метода таким грязным способом.
Я думал: поскольку все интерфейсы всех версий JAR
тот же, я не мог это сделать, перезагрузив другую версию
из JAR (без этого имени-invokation?).
Примечание. Я использую последние Java SE (6) и Java EE (5).
Если вы думаете, что я пытаюсь достичь, это невозможно,
предложите "обходной путь" или другую концепцию.
Ответы
Ответ 1
Я думаю, что если вы загружаете класс, используя
Class.forName(clsname, init, classloader);
(Javadoc здесь) вы получите экземпляр класса, предоставляемого данным загрузчиком классов. Все загруженные из-за этого класса также будут загружаться через один и тот же загрузчик классов.
Пока вы очень осторожны с объектами, созданными с этого момента (для обеспечения GC), вы должны иметь возможность перезагружать разные версии. Я сделал это раньше с Java 1.3, потребовалось много отладки, но в конце у меня было приложение "bootstrap", которое загрузило класс Runnable
по имени и смогло "перезагрузить", создав экземпляр нового загрузчика классов против другого URL-адреса и снова.
Ответ 2
Вы можете программно изменить свой класс, чтобы отразить изменения JAR.
Вот как я это сделаю:
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method m = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
m.setAccessible(true);
m.invoke(urlClassLoader, jarFile.toURI().toURL());
String cp = System.getProperty("java.class.path");
if (cp != null) {
cp += File.pathSeparatorChar + jarFile.getCanonicalPath();
} else {
cp = jarFile.toURI().getPath();
}
System.setProperty("java.class.path", cp);
где jarFile - это версия jar, которую вы хотите использовать/перезаписать.
Ответ 3
Вы можете использовать пакет с открытым исходным кодом: JclLoader, который помогает при загрузке разных версий одной и той же банки. Это также необходимо в одной из наших систем для тестирования.
Ссылка: http://sourceforge.net/projects/jcloader/
Ответ 4
OSGi - это структура, которая позволит вам это сделать. JSR 277, система Java Module предназначена для этого (я думаю). Я не следовал дискуссиям OSGi -vs-JSR 277, поэтому я не знаю, что они вообще пытаются их вывести.
Вы можете перевернуть свой собственный с помощью загрузчиков классов, но это будет менее "весело".
Ответ 5
Да. Я видел это на конференции NFJS. Это то, как такие вещи, как веб-контейнеры, поддерживают горячее развертывание приложений и предполагает использование возможностей классных загрузчиков. Для этого вам нужно создать новый загрузчик классов и использовать его для загрузки библиотеки, о которой идет речь. Затем выбросите загрузчика (или нет) и создайте другой, когда вы хотите перезагрузить. Возможно, вам также придется переопределить поведение загрузчика классов (я помню что-то о загрузчиках классов, получающих классы через их родителя по умолчанию.) Кроме того, я помню предупреждение о том, что объекты, созданные разными загрузчиками классов, не совместимые (не одного типа) друг с другом, даже если файл .class точно такой же.
В основном это глубокая магия.; -)
Ответ 6
Наверное, нет. Java-загрузчик классов не поддерживает загрузку во время выполнения; даже доступными загрузчиками классов являются хаки, которые используют прокси-объект.