Java, Classpath, Classloading => несколько версий одного jar / проекта


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

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar

является ли загрузчик классов достаточно умным, чтобы каким-то образом отделить их? Скорее всего, нет? Как загрузчик классов обрабатывает это, если класс одинаков во всех трех банках. Какой из них загружен и почему?

только загрузчик классов пикап ровно одна банка или он смешивает классы произвольно? Так, например, если класс загружен из версии-1.jar, все другие классы, загруженные из того же загрузчика классов, будут все идти в ту же банку?

Как вы справляетесь с этой проблемой?

есть ли какой-то трюк, чтобы каким-то образом "включить" банки в "требуется.jar "так что они рассматриваются как" один блок/пакет " по Classloader, или как-то связаны?

4 104

4 ответа:

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

  • загрузчиков в приложении обычно больше чем один. Загрузчик класса начальной загрузки делегатов на соответствующие. При создании экземпляра нового класса вызывается более конкретный загрузчик классов. Если он не находит ссылку на класс, который вы пытаетесь загрузить, он делегирует ее родительскому элементу и т. д., Пока вы не доберетесь до класса bootstrap погрузчик. Если ни один из них не найдет ссылку на класс, который вы пытаетесь загрузить, вы получите исключение ClassNotFoundException.

  • Если у вас есть два класса с одинаковым двоичным именем, доступные для поиска одним и тем же загрузчиком классов, и вы хотите знать, какой из них вы загружаете, вы можете только проверить, как конкретный загрузчик классов пытается разрешить имя класса.

  • согласно спецификации языка java, для класса не существует ограничения уникальности двоичное имя, но, насколько я вижу, оно должно быть уникальным для каждого загрузчика классов.

Я могу найти способ загрузить два класса с одинаковым двоичным именем, и это включает в себя их загрузку (и все их зависимости) двумя разными загрузчиками классов, переопределяющими поведение по умолчанию. Грубый пример:

    ClassLoader loaderA = new MyClassLoader(libPathOne);
    ClassLoader loaderB = new MyClassLoader(libPathTwo);
    Object1 obj1 = loaderA.loadClass("first.class.binary.name", true)
    Object2 obj2 = loaderB.loadClass("second.class.binary.name", true);

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

каждый classload выбирает ровно один класс. Обычно первый найденный.

OSGi призван решить проблему нескольких версий одной и той же банке. Эквинокс и Apache Felix являются общими реализациями с открытым исходным кодом для OSGi.

Classloader будет загружать классы из jar, который оказался в classpath первым. Как правило, несовместимые версии библиотеки будут иметь разницу в пакетах, Но в маловероятном случае они действительно несовместимы и не могут быть заменены одним - попробуйте jarjar.

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

вы, вероятно, столкнетесь LinkageErrors заявляя, что повторяющиеся определения классов были обнаружены для загрузчиков классов, как правило, не пытаются определить, какой класс должен быть загружен первым (если в пути к классам загрузчика присутствуют два или более классов с одинаковыми именами). Иногда загрузчик классов загружает первый класс, встречающийся в пути к классам, и игнорирует повторяющиеся классы, но это зависит от реализации загрузчика.

рекомендуется использовать отдельный загрузчик классов для каждого набора библиотек, которые имеют конфликтующие зависимости. Таким образом, если загрузчик классов пытается загрузить классы из a библиотека, зависимые классы будут загружены тем же загрузчиком классов, который не имеет доступа к другим библиотекам и зависимостям.