Как я должен загружать Jars динамически во время выполнения? - PullRequest
276 голосов
/ 13 сентября 2008

Почему так сложно сделать это на Java? Если вы хотите иметь какую-либо модульную систему, вы должны иметь возможность загружать банки динамически. Мне сказали, что есть способ сделать это, написав свой собственный ClassLoader, но это большая работа для чего-то, что должно (по крайней мере, на мой взгляд) быть таким же простым, как вызов метода с файлом jar в качестве аргумента.

Какие-нибудь предложения для простого кода, который делает это?

Ответы [ 15 ]

3 голосов
/ 10 октября 2018

Другое рабочее решение, использующее Инструментарий, которое работает для меня. Он имеет преимущество в изменении поиска загрузчика классов, избегая проблем с видимостью классов для зависимых классов:

Создать класс агента

В этом примере он должен находиться на том же банке, который вызывается из командной строки:

package agent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class Agent {
   public static Instrumentation instrumentation;

   public static void premain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void agentmain(String args, Instrumentation instrumentation) {
      Agent.instrumentation = instrumentation;
   }

   public static void appendJarFile(JarFile file) throws IOException {
      if (instrumentation != null) {
         instrumentation.appendToSystemClassLoaderSearch(file);
      }
   }
}

Изменить MANIFEST.MF

Добавление ссылки на агента:

Launcher-Agent-Class: agent.Agent
Agent-Class: agent.Agent
Premain-Class: agent.Agent

Я на самом деле использую Netbeans, поэтому этот пост помогает узнать, как изменить manifest.mf

Запуск

Launcher-Agent-Class поддерживается только в JDK 9+ и отвечает за загрузку агента без явного определения его в командной строке:

 java -jar <your jar>

В JDK 6+ работает аргумент -javaagent:

java -javaagent:<your jar> -jar <your jar>

Добавление нового Jar во время выполнения

Затем вы можете добавить jar по мере необходимости, используя следующую команду:

Agent.appendJarFile(new JarFile(<your file>));

Я не нашел никаких проблем с использованием этого в документации.

3 голосов
/ 03 октября 2018

Еще одна версия хакерского решения от Allain, которая также работает на JDK 11:

File file = ...
URL url = file.toURI().toURL();
URLClassLoader sysLoader = new URLClassLoader(new URL[0]);

Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[]{url});

В JDK 11 он дает некоторые предупреждения об устаревании, но служит временным решением для тех, кто использует решение Allain в JDK 11.

2 голосов
/ 21 октября 2018

Это может быть поздний ответ, я могу сделать это следующим образом (простой пример для fastutil-8.2.2.jar), используя класс jhplot.Web из DataMelt (http://jwork.org/dmelt)

import jhplot.Web;
Web.load("http://central.maven.org/maven2/it/unimi/dsi/fastutil/8.2.2/fastutil-8.2.2.jar"); // now you can start using this library

Согласно документации, этот файл будет загружен внутри "lib / user" и затем динамически загружен, так что вы можете сразу начать использовать классы из этого jar-файла в той же программе.

1 голос
/ 25 мая 2018

пожалуйста, посмотрите на этот проект, который я начал: proxy-object lib

Эта библиотека загрузит jar из файловой системы или любого другого места. Он будет посвящен загрузчику классов для jar, чтобы убедиться, что нет библиотечных конфликтов. Пользователи смогут создать любой объект из загруженного фляги и вызвать любой метод на нем. Эта библиотека была разработана для загрузки jar-файлов, скомпилированных в Java 8, из базы кода, поддерживающей Java 7.

Чтобы создать объект:

    File libDir = new File("path/to/jar");

    ProxyCallerInterface caller = ObjectBuilder.builder()
            .setClassName("net.proxy.lib.test.LibClass")
            .setArtifact(DirArtifact.builder()
                    .withClazz(ObjectBuilderTest.class)
                    .withVersionInfo(newVersionInfo(libDir))
                    .build())
            .build();
    String version = caller.call("getLibVersion").asString();

ObjectBuilder поддерживает фабричные методы, вызов статических функций и реализации интерфейса обратного вызова. я буду публиковать больше примеров на странице readme.

0 голосов
/ 23 сентября 2011

Лично я считаю, что java.util.ServiceLoader выполняет свою работу довольно хорошо. Вы можете получить пример здесь .

...