Исследование разрешения конфликтов пакетов Jar

Java контейнер

В работе, особенно при реализации промежуточного ПО, часто встречаются конфликты пакетов jar.В этой статье будут проанализированы существующие в отрасли решения по изоляции пакетов jar.

1. Суть конфликта пакетов jar

По какой-то причине приложение Java не загружает правильные классы и ведет себя не так, как ожидалось.

2. Два случая конфликта пакетов jar

Первый тип проблемы конфликта пакетов jar (разные версии одного и того же пакета jar)

  1. Существует несколько версий одного и того же пакета Jar, от которого зависит приложение, и выбрана неправильная версия, из-за чего JVM не загружает требуемые классы или загружает неправильную версию класса.
  2. Для возникновения этой проблемы есть три необходимых условия:
  • Несколько версий одного и того же jar-файла отображаются в дереве зависимостей.
  • Интерфейс изменился между несколькими версиями пакета jar (имя класса, изменение подписи метода, изменение поведения метода).
  • механизм арбитража maven выбирает неправильную версию

Второй тип проблемы конфликта пакетов jar (разные пакеты jar имеют разные версии одного и того же класса)

  1. Один и тот же класс (полное имя класса точно такое же) появляется в нескольких различных зависимых пакетах Jar, то есть существует несколько версий класса, и из-за порядка, в котором загружаются пакеты Jar (самый короткий Maven принцип приоритета пути и покрытия) JVM загрузила неправильную версию класса. Например: предположим, что естьA,B,CДля трех пакетов jar из-за длины пути, от которого зависит пакет Jar, порядка объявлений или порядка загрузки файлов файловой системы, загрузчик классов сначала запускается из пакета Jar.AПосле того, как класс загружен в jar, класс в остальных пакетах jar не будет загружен.
  2. Для возникновения этой проблемы есть три необходимых условия:
  • Один и тот же класс M присутствует в двух (или более двух) разных упаковках банок A и B.
  • Класс M имеет отличия от A и B и ведет себя по-разному.
  • Загруженный класс M — это не то, что нам нужно.

3. Решения

Способ 1: ручное расследование

  1. Определите имя класса, вызвавшего конфликт, на основе информации о стеке исключений.
  2. Используйте mvn dependency:tree -Dverbose -Dincludes=:, чтобы увидеть, где была введена версия пакета jar.
  3. Если это первый тип конфликта пакетов Jar, вы можете использовать для исключения нежелательных версий пакетов Jar или объявить версию в управлении зависимостями .
  4. Если это второй тип конфликта пакетов Jar, если его можно исключить, используйте<excludes>Удалите ненужный пакет Jar. Если его нельзя слить, подумайте об обновлении пакета Jar или замените его отдельным пакетом Jar.

Способ 2: реализовать изоляцию с помощью пользовательского ClassLoaderв блогеРеализация изолированных контейнеров в JavaУпоминается, что каждый пакет jar рассматривается как несколько пакетов, выполняется изолированно через настраиваемый загрузчик классов, а также может понимать, что несколько пакетов jar имеют общий класс. Демо запускается путем запуска класса KContainer, который в основном содержит BundleList и SharedClassList. Каждый Bundle представляет собой пакет jar или путь к классу. Класс Bundle содержит собственный класс BundleClassLoader (наследующий UrlClassLoader). Из-за разных BundleBundleClassLoader его можно реализоватьработать в изоляции, и этот BundleClassLoader должен пройти в SharedClassList.Когда загрузчик классов загружает класс, если он не загружен, он может быть загружен из SharedClassList, переданного извне, что достигаетсяНесколько пакетов jar имеют общий класс.

protected Class<?> findClass(String name) throws ClassNotFoundException {
   logger.debug(“try find class {}”, name);
   Class<?> claz = null;
   try {
     claz = super.findClass(name);
  } catch (ClassNotFoundException e) {
     claz = null;
  }
   if (claz != null) {
     logger.debug(“load from class path for {}”, name);
     return claz;
  }
   //如果没有加载到,从共享的类中加载
   claz = sharedClasses.get(name);
   if (claz != null) {
     logger.debug(“load from shared class for {}”, name);
     return claz;
  }
   logger.warn(“not found class {}”, name);
   throw new ClassNotFoundException(name);
}

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

Способ 3: Легкий изолированный контейнер SOFAArkSOFAArk также использует разные загрузчики классов для загрузки конфликтующих сторонних зависимостей, чтобы они могли сосуществовать при запуске одного и того же приложения.SOFAArk различает, какие зависимые пакеты в приложении должны быть загружены отдельным загрузчиком классов через плагин Ark.. С подключаемым модулем упаковки maven, официально предоставляемым SOFABoot, разработчики могут упаковывать несколько распространенных пакетов JAR в подключаемый модуль Ark для зависимостей приложений или преобразовывать обычные модули Java в подключаемый модуль Ark. Приложение вводит подключаемый модуль Ark, добавляя зависимости maven.Во время выполнения платформа SOFAArk автоматически определяет, содержит ли сторонний пакет зависимостей приложения подключаемый модуль Ark, а затем использует отдельный загрузчик классов для его загрузки. Его логическая схема времени выполнения выглядит следующим образом:

image-20180807120802894

  1. Контейнер SOFAARk находится внизу и отвечает за запуск приложений.
  2. Каждый плагин Ark загружается контейнером SOFAArk с использованием независимого загрузчика классов, изолированных друг от друга.
  3. Бизнес-код приложения и другие распространенные сторонние пакеты зависимостей, отличные от Ark Plugin, вместе именуются Ark Biz. Нужно полагаться на нижний плагин Ark.

В файле POM плагина Ark будет настроена конфигурация экспортируемых и импортируемых классов. Экспорт классов означает экспорт классов в плагине Ark, чтобы они были видны в Ark Biz и других плагинах Ark. Для плагина Ark, если вам нужно использовать экспортированный класс другого плагина Ark, он должен быть объявлен как собственный класс импорта.

Метод 4: Изолирующий контейнер Пандоры АлиPandora Али имеет закрытый исходный код, и в Интернете относительно мало информации. Из PPT выступления Али можно узнать, что Pandora по-прежнему основана на недостатках изолированных контейнеров, таких как Pandora, реализованных ClassLoader:

image-20180807120821154

  1. Использование сложное и трудное для понимания.
  2. Медленный запуск, пользователи не могут выбирать по требованию
  3. Сложность отладки
  4. Сложность развертывания и эксплуатации

Следующая статья будет посвящена использованию контейнера Ant Financial SOFA-ARK и анализу исходного кода.