Я все еще относительно новичок в Java, поэтому, пожалуйста, потерпите меня.
Моя проблема в том, что мое Java-приложение зависит от двух библиотек. Давайте назовем их Библиотека 1 и Библиотека 2. Обе эти библиотеки имеют взаимную зависимость от Библиотеки 3. Однако:
- Библиотека 1 требует именно версию 1 библиотеки 3.
- Библиотека 2 требует именно версию 2 Библиотеки 3.
Это в точности определение JAR hell (или хотя бы одного его варианта).
Как указано в ссылке, я не могу загрузить обе версии третьей библиотеки в один и тот же загрузчик классов. Таким образом, я пытался выяснить, смогу ли я создать новый загрузчик классов в приложении для решения этой проблемы. Я искал 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 во время выполнения?
Буду признателен за любую помощь по этой проблеме.