Динамическая загрузка Jar сбивает меня с толку - PullRequest
2 голосов
/ 28 июля 2010

Концептуально, что я делаю, когда загружаю новую банку?Является ли URLClassloader единственным выбором?Как мне сформировать эти URL, чтобы они указывали на подкаталог, содержащий больше jar-файлов.

Если кто-то чувствует себя супер-щедрым, некоторый демонстрационный код для выполнения следующего будет действительно полезным (предположим, что «jars / A.jar» содержит «myClass», который мы хотим создать):

  1. загрузить JAR-файл из подкаталога
  2. вернуть из него определенный класс
  3. создать экземпляр этого класса

Ответы [ 4 ]

4 голосов
/ 28 июля 2010

Концептуально, что я делаю, когда загружаю новую банку?

Концептуально, вы не загружаете новый JAR. Скорее, вы определяете загрузчик классов, который будет загружать код и другие ресурсы из файла JAR по запросу.

Является ли URLClassloader единственным выбором?

Теоретически, вы можете реализовать свой собственный подкласс ClassLoader, но это не обязательно для того, что вы пытаетесь сделать.

Как мне сформировать эти URL, чтобы они указывали на подкаталог, содержащий больше jar-файлов.

Я думаю, это суть вашей проблемы. Конструкторы URLClassLoader интерпретируют аргумент URL[] следующим образом:

«Предполагается, что любой URL, заканчивающийся символом« / », ссылается на каталог. В противном случае предполагается, что URL ссылается на файл JAR, который будет загружен и открыт при необходимости."

В первом случае приведенного выше текста предполагается, что каталог является корнем дерева, содержащего ресурсы для загрузки.

Но я понимаю, что вы пытаетесь настроить загрузчик классов, который будет загружать из всех файлов JAR в данном каталоге. Для этого вам необходимо:

  1. Считайте каталог и создайте список объектов File для любых файлов JAR.
  2. Создать массив для хранения одинакового количества URL экземпляров.
  3. Для каждого файла JAR File, используйте File.toURL(), чтобы создать URL и добавить в массив. (Использование File.toURL() означает, что вы получите кошерные URL «file:» для файлов JAR, которые будут работать на вашей платформе.)
  4. Создайте URLClassLoader, используя массив URL.
2 голосов
/ 28 июля 2010

Действительно короткая версия: когда вы загружаете новый JAR, вы предоставляете Java новое место для поиска классов.

Для более подробного объяснения в Википедии есть статья, относящаяся к этому: http://en.wikipedia.org/wiki/Java_Classloader

Также можно попробовать: http://onjava.com/pub/a/onjava/2005/01/26/classloading.html

URLClassLoader - не единственный ваш выбор; проверить http://download -llnw.oracle.com / javase / учебное пособие / развертывание / jar / apiindex.html

Re: как должны формироваться URL, из документации Java (http://download -llnw.oracle.com / javase / 7 / docs / api / java / net / URLClassLoader.html ):

Этот загрузчик классов используется для загрузки классов и ресурсов из пути поиска URL-адресов, относящихся как к файлам JAR, так и к каталогам. Предполагается, что любой URL, заканчивающийся символом «/», ссылается на каталог. В противном случае предполагается, что URL ссылается на файл JAR, который будет открыт при необходимости.

Адаптировано с Как динамически загружать Jars во время выполнения? :

URLClassLoader child = new URLClassLoader (new URL("jars/A.jar"), this.getClass().getClassLoader());
Class classToLoad = Class.forName ("com.myClass", true, child);
Method method = classToLoad.getDeclaredMethod ("myMethod");
Object instance = classToLoad.newInstance ();
Object result = method.invoke (instance);

Кроме того, в ответе, приведенном выше, перечислены еще несколько альтернатив URLClassLoader (они упоминают OSGi, JCL и некоторые другие, ни с одним из которых у меня нет опыта, или я бы кое-что сказал здесь).

НТН

0 голосов
/ 06 августа 2010

Простой способ - убедиться, что зависимые файлы JAR находятся в пути к классам, перед запуском программы.


Если вы хотите загрузить из подкаталога, вам нужно определить путь к JAR-файлу, который нужно указать URLClassLoader. Вы можете использовать что-то вроде этого, чтобы получить путь к вашей программе:

new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath())

Это что-то вроде хака, но я не знаю ни одного официального способа получить путь к Java-программе.


Если вы хотите динамически загрузить JAR, чтобы вы могли использовать его так, как если бы вы указали его в пути к классам, проверьте этот поток . Это определенно относится к сфере неподдерживаемого взлома, но это также просто и легко.

Модифицированная версия, которую я использую:

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Hack to modify classpath at runtime. Can be used to load JARs as if they were
 * loaded from the start.
 * 
 * Warning: May break if URLClassLoader changes
 * 
 * @author Antony Miguel
 */
public class ClasspathHacker {
    /**
     * Add a file to the classpath.
     * 
     * @param pPath
     *            path to the file
     */
    public static void addFile(String pPath) {
        File f = new File(pPath);
        addFile(f);
    }

    /**
     * Add a file to the classpath.
     * 
     * @param pFile
     *            the file to be added
     */
    public static void addFile(File pFile) {
        try {
            addURL(pFile.toURI().toURL());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Add the content pointed to by a URL to the classpath.
     * 
     * @param pURL
     *            the URL pointing to the content to be added
     */
    public static void addURL(URL pURL) {
        /*
         * Use reflection to call addURL on the system classloader, which we
         * expect to be a URLClassLoader
         */
        Method method;
        try {
            method = URLClassLoader.class.getDeclaredMethod("addURL",
                    new Class[] { URL.class });
            method.setAccessible(true);
            method.invoke(ClassLoader.getSystemClassLoader(),
                    new Object[] { pURL });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
0 голосов
/ 28 июля 2010

Если список jar известен, то потенциально лучшим или более простым решением было бы просто добавить запись Class-Path в манифест родительского jar. Таким образом, вы полностью избегаете использования пользовательских загрузчиков классов.

Class-Path: jars/A.jar

Очевидно, что это не сработает, когда зависимые банки не известны во время сборки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...