2013-12-14 2 views
1

Я создаю клиентское серверное приложение. Во время выполнения клиентское приложение загружает файл jar из серверного приложения и сохраняет его. Я запускаю как клиент, так и серверное приложение как файлы jar. Теперь я хочу загрузить класс, содержащийся в этом загруженном файле jar.Как загрузить неизвестный класс из загруженного файла jar во время выполнения?

Например, у меня есть интерфейс A и класс B, реализующий A. Клиентское приложение не знает о классе B, его имени или даже его существовании. После запуска клиентского приложения клиентские приложения загружают файл jar, содержащий файл jar с контентом: server/package/B.class, где сервер и пакет являются папками.

Теперь клиентское приложение должно загрузить этот класс B из загруженного файла JAR со следующим кодом:

URL downloadURL = downloadFolder.toURI().toURL(); 
URL[] downloadURLs = new URL[] { ruleSetFolderURL }; 
URLClassLoader loader = 
    new URLClassLoader(downloadURLs); 
Class tmp = loadClass(server.package.B); 

Но тогда я получаю ClassNotFoundException в последней строке. Должен ли я сначала извлечь файл jar? структура папок в файле jar похожа на структуру папок в каталоге bin серверного приложения.

+0

Но клиент не знает имя конкретного имени класса, который он хочет загрузить, поэтому, где я могу получить "NameOfClassThatContainsTheCode" из. Клиентское приложение просто знает, что хочет загрузить загруженный класс, и этот класс реализует интерфейс A – siebenschlaefer

ответ

5

Чтобы загрузить класс динамически из JAR-файла, который реализует определенный интерфейс, но вы не знаете заранее, какой класс, который будет быть и сама баночка файл не определяет класса по умолчанию «плагин», вы можете перебирать скаченную банку и получить список классов, содержащихся в банке, как это:

/** 
    * Scans a JAR file for .class-files and returns a {@link List} containing 
    * the full name of found classes (in the following form: 
    * packageName.className) 
    * 
    * @param file 
    * JAR-file which should be searched for .class-files 
    * @return Returns all found class-files with their full-name as a List of 
    *   Strings 
    * @throws IOException If during processing of the Jar-file an error occurred 
    * @throws IllegalArgumentException If either the provided file is null, does 
    *         not exist or is no Jar file 
    */ 
    public List<String> scanJarFileForClasses(File file) throws IOException, IllegalArgumentException 
    { 
      if (file == null || !file.exists()) 
        throw new IllegalArgumentException("Invalid jar-file to scan provided"); 
      if (file.getName().endsWith(".jar")) 
      { 
        List<String> foundClasses = new ArrayList<String>(); 
        try (JarFile jarFile = new JarFile(file)) 
        { 
          Enumeration<JarEntry> entries = jarFile.entries(); 
          while (entries.hasMoreElements()) 
          { 
            JarEntry entry = entries.nextElement(); 
            if (entry.getName().endsWith(".class")) 
            { 
              String name = entry.getName(); 
              name = name.substring(0,name.lastIndexOf(".class")); 
              if (name.indexOf("/")!= -1) 
                name = name.replaceAll("/", "."); 
              if (name.indexOf("\\")!= -1) 
                name = name.replaceAll("\\", "."); 
              foundClasses.add(name); 
            } 
          } 
        } 
        return foundClasses; 
      } 
      throw new IllegalArgumentException("No jar-file provided"); 
    } 

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

/** 
    * <p> 
    * Looks inside a jar file and looks for implementing classes of the provided interface. 
    * </p> 
    * 
    * @param file 
    * The Jar-File containing the classes to scan for implementation of the given interface 
    * @param iface 
    * The interface classes have to implement 
    * @param loader 
    * The class loader the implementing classes got loaded with 
    * @return A {@link List} of implementing classes for the provided interface 
    * inside jar files of the <em>ClassFinder</em>s class path 
    * 
    * @throws Exception If during processing of the Jar-file an error occurred 
    */ 
    public List<Class<?>> findImplementingClassesInJarFile(File file, Class<?> iface, ClassLoader loader) throws Exception 
    { 
     List<Class<?>> implementingClasses = new ArrayList<Class<?>>(); 
     // scan the jar file for all included classes 
     for (String classFile : scanJarFileForClasses(file)) 
     { 
      Class<?> clazz; 
      try 
      { 
       // now try to load the class 
       if (loader == null) 
        clazz = Class.forName(classFile); 
       else 
        clazz = Class.forName(classFile, true, loader); 

       // and check if the class implements the provided interface 
       if (iface.isAssignableFrom(clazz) && !clazz.equals(iface)) 
        implementingClasses.add(clazz); 
      } 
      catch (ClassNotFoundException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
     return implementingClasses; 
    } 

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

public void executeImplementationsOfAInJarFile(File downloadedJarFile) 
{ 
    If (downloadedJarFile == null || !downloadedJarFile.exists()) 
     throw new IllegalArgumentException("Invalid jar file provided"); 

    URL downloadURL = downloadedJarFile.toURI().toURL(); 
    URL[] downloadURLs = new URL[] { downloadURL }; 
    URLClassLoader loader = URLClassLoader.newInstance(downloadURLs, getClass().getClassLoader()); 
    try 
    { 
     List<Class<?>> implementingClasses = findImplementingClassesInJarFile(downloadedJarFile, A.class, loader); 
     for (Class<?> clazz : implementingClasses) 
     { 
      // assume there is a public default constructor available 
      A instance = clazz.newInstance(); 
      // ... do whatever you like here 
     } 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace(); 
    } 
} 

Обратите внимание, что в этом примере предполагается, что A является интерфейсом. Если в Jar-файле не может быть реализован класс реализации, файл jar будет загружен загрузчиком классов, но реализация объекта не произойдет.

Обратите внимание, что всегда рекомендуется обеспечить родительский загрузчик классов - особенно с помощью URLClassLoader. Кроме того, может случиться, что некоторые классы, которые не содержатся в Jar-файле, могут отсутствовать, и поэтому вы получите ClassNotFoundException при попытке получить к ним доступ. Это связано с механизмом делегирования, используемым загрузчиками классов, которые сначала спрашивают своего родителя, знают ли они определение класса для требуемого класса. Если это так, класс будет загружен родителем; если нет, класс будет загружен созданным URLClassLoader.

Помните, что возможна загрузка одного и того же класса несколькими разными классами ClassLoaders (одноранговые загрузчики классов).Но хотя имя и байты класса могут быть одинаковыми, классы несовместимы с использованием разных экземпляров класса loader, поэтому попытка передать экземпляр, загруженный classloder A, в тип, загруженный загрузчиком классов B, завершится с ошибкой.

@Edit: изменен код, чтобы исключить возвращаемые значения null, вместо этого выбрасываются более или менее соответствующие исключения. @ Edit2: поскольку я не могу принять предложения по рассмотрению кода, я отредактировал обзор непосредственно в сообщении

1

Чтобы загрузить банку на нужды ссылаться на URL к загруженному файлу на диске или в сети:

ClassLoader loader = URLClassLoader.newInstance(
    new URL[]{new File("my.jar").toURI().toURL()}, 
    getClass().getClassLoader() 
); 

Class<?> clazz = Class.forName("mypackage.MyClass", true, loader); 

Больше в пост: How to load a jar file at runtime.

Update

Как сканировать банку с Отражения: How to scan JAR's, which are loaded at runtime, with Google reflections library?

+0

, но мое клиентское приложение не знает, что загруженный класс является классом B. Поэтому я могу просто выполнить A.getClass(). GetClassLoader (), но это не сработает. – siebenschlaefer

+0

Ну, вы должны знать что-то об этом классе. То есть расширенный интерфейс, пакет, аннотация для определения класса. Затем вы можете сканировать файл вручную или с помощью библиотеки, например Reflections, или вручную в виде архива jar, ищущего класс. –

+0

Добавлена ​​ссылка на пример Reflections для ответа. –

Смежные вопросы