Почему так сложно сделать это на Java? Если вы хотите иметь какую-либо модульную систему, вы должны иметь возможность загружать файлы JAR динамически. Мне сказали, что есть способ сделать это, написав свой собственный ClassLoader
, но это большая работа для чего-то, что должно (по крайней мере, на мой взгляд) быть таким же простым, как вызов метода с файлом JAR в качестве аргумента.
Любые предложения для простого кода, который делает это?
java
jar
classloader
Аллен Лалонд
источник
источник
Ответы:
Причина, по которой это сложно - безопасность. Загрузчики классов должны быть неизменными; Вы не должны иметь возможность произвольно добавлять к нему классы во время выполнения. Я на самом деле очень удивлен, что работает с системным загрузчиком классов. Вот как вы делаете свой собственный дочерний загрузчик классов:
Больно, но это так.
источник
URLClassLoader child = new URLClassLoader (new URL[] {new URL("file://./my.jar")}, Main.class.getClassLoader());
предполагая, что файл JAR называетсяmy.jar
и находится в том же каталоге.Следующее решение является хакерским, поскольку оно использует отражение, чтобы обойти инкапсуляцию, но оно работает безупречно:
источник
URLClassLoader.class.getDeclaredMethod("addURL", URL.class)
это нелегальное использование рефлексии и в будущем произойдет сбой.Вы должны взглянуть на OSGi , например, реализованный в платформе Eclipse . Это именно так. Вы можете устанавливать, удалять, запускать и останавливать так называемые пакеты, которые фактически являются файлами JAR. Но он делает немного больше, поскольку предлагает, например, сервисы, которые могут быть динамически обнаружены в файлах JAR во время выполнения.
Или посмотрите спецификацию для системы модулей Java .
источник
Как насчет платформы загрузчика классов JCL ? Я должен признать, я не использовал это, но это выглядит многообещающим.
Пример использования:
источник
Вот версия, которая не является устаревшей. Я изменил оригинал, чтобы удалить устаревшую функциональность.
источник
В то время как большинство решений, перечисленных здесь, являются либо взломами (до JDK 9), которые сложно настроить (агенты), или просто больше не работают (после JDK 9), я нахожу действительно шокирующим, что никто не упомянул четко документированный метод .
Вы можете создать собственный загрузчик классов системы, и тогда вы сможете делать все, что пожелаете. Отражение не требуется, и все классы используют один и тот же загрузчик классов.
При запуске JVM добавьте этот флаг:
Загрузчик классов должен иметь конструктор, принимающий загрузчик классов, который должен быть установлен в качестве его родителя. Конструктор будет вызываться при запуске JVM, и будет передан настоящий системный загрузчик классов, основной класс будет загружен пользовательским загрузчиком.
Чтобы добавить банки, просто позвоните
ClassLoader.getSystemClassLoader()
и приведите их к своему классу.Проверьте эту реализацию для тщательно созданного загрузчика классов. Обратите внимание, вы можете изменить
add()
метод на публичный.источник
С Java 9 ответы
URLClassLoader
теперь дают ошибку вроде:Это потому, что используемые загрузчики классов изменились. Вместо этого, чтобы добавить в системный загрузчик классов, вы можете использовать API инструментария через агента.
Создайте класс агента:
Добавьте META-INF / MANIFEST.MF и поместите его в файл JAR с классом агента:
Запустите агент:
Здесь используется библиотека byte-buddy-agent для добавления агента в работающую JVM:
источник
Лучшее, что я нашел, - это org.apache.xbean.classloader.JarFileClassLoader, который является частью проекта XBean .
Вот короткий метод, который я использовал в прошлом, чтобы создать загрузчик классов из всех файлов lib в определенном каталоге
Затем, чтобы использовать загрузчик классов, просто выполните:
источник
Если вы работаете на Android, работает следующий код:
источник
Вот быстрый обходной путь для метода Аллена, чтобы сделать его совместимым с более новыми версиями Java:
Обратите внимание, что он основан на знании внутренней реализации конкретной JVM, поэтому он не идеален и не является универсальным решением. Но это быстрый и простой обходной путь, если вы знаете, что собираетесь использовать стандартный OpenJDK или Oracle JVM. Это может также сломаться в некоторый момент в будущем, когда будет выпущена новая версия JVM, так что вам нужно помнить об этом.
источник
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make void jdk.internal.loader.ClassLoaders$AppClassLoader.appendToClassPathForInstrumentation(java.lang.String) accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @18ef96
Решение, предложенное Джодоннеллом, хорошо, но должно быть немного улучшено. Я использовал этот пост, чтобы успешно разработать свое приложение.
Назначить текущий поток
Во-первых, мы должны добавить
или вы не сможете загрузить ресурс (такой как spring / context.xml), хранящийся в банке.
Не включать
ваши фляги в загрузчик родительского класса или вы не сможете понять, кто что загружает.
см. также Проблема перезагрузки банки с помощью URLClassLoader
Тем не менее, OSGi Framework остается лучшим способом.
источник
Еще одна версия хакерского решения от Allain, которая также работает на JDK 11:
В JDK 11 он дает некоторые предупреждения об устаревании, но служит временным решением для тех, кто использует решение Allain в JDK 11.
источник
Другое рабочее решение, использующее Инструментарий, которое работает для меня. Он имеет преимущество в изменении поиска загрузчика классов, избегая проблем с видимостью классов для зависимых классов:
Создать класс агента
В этом примере он должен находиться в том же банке, который вызывается из командной строки:
Изменить MANIFEST.MF
Добавляем ссылку на агента:
Я на самом деле использую Netbeans, так что этот пост помогает о том, как изменить manifest.mf
Бег
Поддерживается
Launcher-Agent-Class
только в JDK 9+ и отвечает за загрузку агента без явного определения его в командной строке:То, как работает JDK 6+, определяет
-javaagent
аргумент:Добавление нового Jar во время выполнения
Затем вы можете добавить jar по мере необходимости, используя следующую команду:
Я не нашел никаких проблем с использованием этого в документации.
источник
В случае, если кто-то ищет это в будущем, этот способ работает для меня с OpenJDK 13.0.2.
У меня есть много классов, которые мне нужно динамически создавать во время выполнения, каждый из которых может иметь свой путь к классу.
В этом коде у меня уже есть объект с именем pack, который содержит метаданные о классе, который я пытаюсь загрузить. Метод getObjectFile () возвращает местоположение файла класса для класса. Метод getObjectRootPath () возвращает путь к каталогу bin /, содержащему файлы классов, в которые входит класс, который я пытаюсь создать. Метод getLibPath () возвращает путь к каталогу, содержащему файлы jar, составляющие classpath для модуля, частью которого является класс.
Я использовал зависимость Maven: org.xeustechnologies: jcl-core: 2.8, чтобы сделать это раньше, но после перехода с JDK 1.8 он иногда зависал и никогда не возвращался, застряв в ожидании ссылок в Reference :: waitForReferencePendingList ().
Я также храню карту загрузчиков классов, чтобы их можно было повторно использовать, если класс, который я пытаюсь создать, находится в том же модуле, что и класс, который я уже создал, что я бы рекомендовал.
источник
пожалуйста, посмотрите на этот проект, который я начал: proxy-object lib
Эта библиотека загрузит jar из файловой системы или любого другого места. Он будет посвящен загрузчику классов для jar, чтобы убедиться, что нет библиотечных конфликтов. Пользователи смогут создать любой объект из загруженного фляги и вызвать любой метод на нем. Эта библиотека была разработана для загрузки jar-файлов, скомпилированных в Java 8, из базы кода, поддерживающей Java 7.
Чтобы создать объект:
ObjectBuilder поддерживает фабричные методы, вызов статических функций и реализации интерфейса обратного вызова. я буду публиковать больше примеров на странице readme.
источник
Это может быть поздний ответ, я могу сделать это так (простой пример для fastutil-8.2.2.jar), используя класс jhplot.Web из DataMelt ( http://jwork.org/dmelt )
Согласно документации, этот файл будет загружен внутри "lib / user" и затем динамически загружен, так что вы можете сразу же начать использовать классы из этого jar-файла в той же программе.
источник
Мне нужно было загрузить файл jar во время выполнения для java 8 и java 9+ (комментарии выше не работают для обеих этих версий). Вот способ сделать это (используя Spring Boot 1.5.2, если это возможно).
источник
Лично я нахожу, что java.util.ServiceLoader выполняет эту работу довольно хорошо. Вы можете получить пример здесь .
источник