Вот

еще относительно новичок в Java, поэтому, пожалуйста, потерпите меня.

Моя проблема в том, что мое Java-приложение зависит от двух библиотек. Давайте назовем их Библиотека 1 и Библиотека 2. Обе эти библиотеки имеют взаимную зависимость от Библиотеки 3. Однако:

Библиотека 1 требует именно версию 1 библиотеки 3.Библиотека 2 требует именно версию 2 библиотеки 3.

Это именно определениеJAR ад (или хотя бы один его вариант). Как указано в ссылке, я не могу загрузить обе версии третьей библиотеки в один и тот же загрузчик классов. Таким образом, я пытался выяснить, смогу ли я создать новый загрузчик классов в приложении для решения этой проблемы. Я смотрел вURLClassLoader, но я не смог понять это.

Вот пример структуры приложения, которая демонстрирует проблему. Класс Main (Main.java) приложения пытается создать экземпляр Library1 и Library2 и запустить некоторый метод, определенный в этих библиотеках:

Main.java (оригинальная версия, перед любой попыткой решения):

public class Main {
    public static void main(String[] args) {
        Library1 lib1 = new Library1();
        lib1.foo();

        Library2 lib2 = new Library2();
        lib2.bar();
    }
}

Library1 и Library2 обе имеют взаимную зависимость от Library3, но Library1 требует именно версию 1, а Library2 требует именно версию 2. В примере обе эти библиотеки просто печатают версию Library3, которую они видят:

Library1.java:

public class Library1 {
  public void foo() {
    Library3 lib3 = new Library3();
    lib3.printVersion();    // Should print "This is version 1."
  }
}

Library2.java:

public class Library2 {
  public void foo() {
    Library3 lib3 = new Library3();
    lib3.printVersion();    // Should print "This is version 2." if the correct version of Library3 is loaded.
  }
}

И затем, конечно, есть несколько версий Library3. Все, что они делают, это печатают номера своих версий:

Версия 1 библиотеки 3 (требуется для библиотеки 1):

public class Library3 {
  public void printVersion() {
    System.out.println("This is version 1.");
  }
}

Версия 2 библиотеки3 (требуется для библиотеки2):

public class Library3 {
  public void printVersion() {
    System.out.println("This is version 2.");
  }
}

Когда я запускаю приложение, classpath содержит Library1 (lib1.jar), Library2 (lib2.jar) и версию 1 библиотеки 3 (lib3-v1 / lib3.jar). Это прекрасно работает для Library1, но не будет работать для Library2.

Что-то, что мне нужно сделать, это заменить версию Library3, которая появляется на пути к классам перед созданием экземпляра Library2. У меня сложилось впечатление, чтоURLClassLoader можно использовать для этого, вот что я попробовал:

Main.java (новая версия, включая мою попытку решения):

import java.net.*;
import java.io.*;

public class Main {
  public static void main(String[] args)
    throws MalformedURLException, ClassNotFoundException,
          IllegalAccessException, InstantiationException,
          FileNotFoundException
  {
    Library1 lib1 = new Library1();
    lib1.foo();     // This causes "This is version 1." to print.

    // Original code:
    // Library2 lib2 = new Library2();
    // lib2.bar();

    // However, we need to replace Library 3 version 1, which is
    // on the classpath, with Library 3 version 2 before attempting
    // to instantiate Library2.

    // Create a new classloader that has the version 2 jar
    // of Library 3 in its list of jars.
    URL lib2_url = new URL("file:lib2/lib2.jar");        verifyValidPath(lib2_url);
    URL lib3_v2_url = new URL("file:lib3-v2/lib3.jar");  verifyValidPath(lib3_v2_url);
    URL[] urls = new URL[] {lib2_url, lib3_v2_url};
    URLClassLoader c = new URLClassLoader(urls);

    // Try to instantiate Library2 with the new classloader    
    Class<?> cls = Class.forName("Library2", true, c);
    Library2 lib2 = (Library2) cls.newInstance();

    // If it worked, this should print "This is version 2."
    // However, it still prints that it's version 1. Why?
    lib2.bar();
  }

  public static void verifyValidPath(URL url) throws FileNotFoundException {
    File filePath = new File(url.getFile());
    if (!filePath.exists()) {
      throw new FileNotFoundException(filePath.getPath());
    }
  }
}

Когда я запускаю это,lib1.foo() вызывает "Это версия 1." быть напечатанным. Так как это версия Library3, которая находится в пути к классам при запуске приложения, это ожидается.

Тем не менее, я ожидалlib2.bar() напечатать «Это версия 2.», отражая, что новая версия Library3 была загружена, но она по-прежнему печатает «Это версия 1.»

Почему использование нового загрузчика классов с загруженной верной версией jar по-прежнему приводит к использованию старой версии jar? Я делаю что-то неправильно? Или я не понимаю концепцию загрузчиков классов? Как правильно переключать jar-версии Library3 во время выполнения?

Буду признателен за любую помощь по этой проблеме.

Ответы на вопрос(7)

Ваш ответ на вопрос