#java #jar #classpath
#java #jar #classpath
Вопрос:
Предположим, у меня есть 2 jar на пути к классу
JAR1:
Class1.class
resource.xml
JAR2:
Class2.class
resource.xml
Классы Class1 и Class2 используют ресурс пути к классам (каждый JAR предоставляет свой собственный resource.xml и хочет его прочитать).
В моем приложении я использую оба jar, и когда загружается Class1 или Class2, resource.xml загрузка происходит случайным образом (одно из двух).
Возможно ли, чтобы Class1 всегда загружал свои resource.xml а класс 2 свой собственный?
Ответ №1:
Я думаю, что невозможно делать именно то, что вы хотите.
Давайте начнем с некоторых фактов о том, как работают загрузчики классов и загрузка ресурсов
-
При вызове
Class.getResource()
он превращается в вызовClass.getClassloader().getResource()
. -
Каждый класс имеет ровно один загрузчик классов.
-
У каждого загрузчика классов есть ровно один родительский загрузчик классов.
-
Способ построения загрузчиков классов гарантирует, что загрузчики классов образуют строгое дерево с так называемым загрузчиком классов начальной загрузки в корне дерева.
-
Загрузчик классов может использовать одну из двух стратегий для поиска материала:
- сначала спросите родительский загрузчик классов, а затем проверьте ресурсы этого загрузчика классов,
- сначала проверьте ресурсы этого загрузчика классов, а затем спросите родительский загрузчик классов.
Теперь, ради аргументации, давайте рассмотрим этот пример:
JAR1:
Class1.class
resource.xml
resource1.xml
JAR2:
Class2.class
resource.xml
resource2.xml
Итак, давайте рассмотрим, как мы могли бы объединить это:
-
Если JAR1 и JAR2 имеют один и тот же загрузчик классов, то у этого загрузчика классов нет способа узнать, какой JAR следует искать первым.
getResource()
Вызов classloader не знает, каким был исходный класс. -
Предположим, что JAR1 и JAR2 имеют разные загрузчики классов:
-
Если загрузчик классов JAR2 был родительским для JAR1, то он может делегировать JAR2, но загрузчик классов JAR2 не может делегировать JAR1. Если бы мы вызвали
Class2.class.getResource("resource1.xml")
classloader Class2, он не смог бы делегировать classloader, который знает о «resource1.xml » и груз вышел бы из строя. -
Если вы перевернете его и сделаете загрузчик классов JAR1 родительским для JAR2, тогда мы обнаружим, что
Class1.class.getResource("resource2.xml")
это приведет к сбою.
-
-
Предположим, что мы создали 2 загрузчика классов, которые ОБА знали о JAR1 и JAR2, и ни один из них не был делегирован другому. Первый загрузчик классов может искать ресурсы в JAR1, а затем в JAR2, а затем в родительском, второй загрузчик классов может искать в JAR2, JAR1, parent .
Пока все в порядке. Но как насчет загрузки класса? Здесь мы должны быть очень осторожны. Мы не можем допустить ситуации, когда Class1.class может быть загружен обоими загрузчиками классов. Потому что, если это произойдет, вы получите два загруженных типа с одинаковым полным именем и разными загрузчиками классов. Но система типов Java говорит, что это дает нам два разных типа … и это может привести к неожиданностям
ClassCastExceptions
. Поэтому мы должны убедиться, что два соответствующих загрузчика классов не загружают один и тот же класс.Но это приводит к другой проблеме. Если первому загрузчику классов предлагается найти класс, который может загружать только другой загрузчик классов, он не может делегировать ему … и конечным результатом будет то, что вы получите ошибку или исключение «класс не найден».
В целом, я думаю, что я рассмотрел все возможные структуры загрузчика классов… и ни один из них не работает для примера, который я предложил.
Возможно, можно «нарушить правила» и настроить эффективную сеть делегирования загрузчика классов без древовидной структуры; например, используя черный ход, чтобы сообщить двум загрузчикам классов друг о друге. Однако существует риск, что вы столкнетесь с циклом делегирования … что приведет к «бесконечной» рекурсии. И даже если бы это сработало, существуют и другие потенциальные риски; например, для модели безопасности Java sandbox, которая также зависит от работы загрузчиков классов.
Не ходите туда. Найдите другой способ сделать это; например, используйте разные имена для двух версий «resource.xml «.
Комментарии:
1. Да, мне не понравилась идея в первую очередь, но меня спросили, возможно ли это; И еще одна вещь — что, если приложение (пользователь jar1 и jar2) имеет свой собственный загрузчик классов (CL) и использует отдельный CL для jar1 и отдельный для jar2? оба изэти CLS могут быть без родительского CL или с JDK CL в качестве родительского. По сути, я знаю, что между jar1 и jar2 не будет соединений — они не используют друг друга. Им просто требуется файл с тем же именем …. 🙁