Управление зависимостями больших Java-систем - PullRequest
30 голосов
/ 21 сентября 2009

У нас есть большая (> 500 000 LOC) Java-система, которая зависит от 40-50 OSS пакеты. Система построена с помощью Ant, а управление зависимостями обрабатывается вручную в настоящее время. Я расследую Айви и / или Мэйвена, чтобы автоматизировать зависимости. Мы смотрели на Maven как на автоматизацию сборки Система в прошлом году и отклонил его, потому что это потребует полностью реструктуризация нашей системы в соответствии с архитектурой Maven. Теперь я стремятся автоматизировать только задачи управления зависимостями.

Я провел некоторые эксперименты с Айви, но столкнулся с проблемами. Например, когда я указываю ActiveMQ в качестве зависимости и говорю Айви использовать POM в репозитории Maven для спецификации зависимостей, Ivy получает кучу пакетов (Jetty, Derby и Geronimo для экземпляр), который я знаю, не нужно просто использовать ActiveMQ.

Если я установлю usepoms = "false" в ivysettings.xml, он получит только activemq.jar, но это, кажется, побеждает цель Айви и переводит его в простой jar-fetcher с зависимостью, созданной вручную технические характеристики.

Здесь есть большая проблема, которая раньше называлась "DLL Hell" в Окна. В некоторых случаях две прямые зависимости первого уровня указывают на разные версии одной и той же транзитивной зависимости (для экземпляр log4j.jar). Только один log4j.jar может быть в пути к классам, поэтому разрешение зависимостей включает в себя ручное определение, какая версия совместим со всеми своими клиентами в нашей системе.

Полагаю, все сводится к качеству зависимости каждого пакета спецификация (ПОМ). В случае ActiveMQ, нет никакой области декларации, поэтому любая ссылка на ActiveMQ загрузит все свои зависимости, если мы вручную не исключаем те, которые мы не знаем хочу.

В случае log4j для автоматического разрешения зависимостей потребуется что все клиенты log4j (другие пакеты, которые зависят от log4j) проверить все предыдущие версии log4j и указать диапазон (или список) совместимых версий log4j в POM. Это наверное тоже много просить.

Это текущее положение вещей, или я что-то упустил?

Ответы [ 8 ]

11 голосов
/ 21 сентября 2009

Вы абсолютно правы, говоря, что

Полагаю, все сводится к качеству спецификации зависимостей каждого пакета (POM).

Единственное, что я хотел бы добавить, - это рассматривать POM или любую другую форму метаданных в качестве отправной точки. Это очень полезно, например, ActiveMQ предоставляет все зависимости для вас, но вам решать, действительно ли он подходит вашему проекту.

В конце концов, даже если принять во внимание версию log4j, у вас будут внешние зависимости, выбрать версию или выбрать версию, которая, как вы знаете, работает для вас?


Что касается того, как вы можете настроить зависимости, вот что вы можете сделать с Ivy:

ненужные пакеты

Ivy получает несколько пакетов (например, Jetty, Derby и Geronimo), которые, как я знаю, не нужны для использования ActiveMQ.

Обычно это происходит из-за плохой модульности в приложении. Некоторой части приложения требуется, например, Jetty, но вы получите эту транзитивную зависимость, даже если вы ее не используете.

Возможно, вы захотите взглянуть на механизм исключения плюща :

<dependency name="A" rev="1.0">
  <exclude module="B"/>
</dependency>

Зависимые версии

В пути к классам может находиться только один log4j.jar, поэтому для разрешения зависимостей необходимо вручную определить, какая версия совместима со всеми его клиентами в нашей системе.

Возможно, я неправильно понимаю это, но в разрешении конфликта Айви нет ручного элемента. Существует список менеджеров конфликтов по умолчанию :

  • все : этот менеджер конфликтов разрешает конфликты, выбирая все ревизии. Также называется NoConflictManager, он выселяет любой модуль.
  • самое позднее : этот диспетчер конфликтов выбирает только «самую последнюю» ревизию, последняя из которых определяется как самая поздняя по времени. Обратите внимание, что вычисление последнего времени является дорогостоящим, поэтому, если возможно, предпочтите последний пересмотр.
  • latest-revision : этот диспетчер конфликтов выбирает только «последнюю» ревизию, последняя из которых определяется путем сравнения строк ревизий.
  • последней совместимости : этот менеджер конфликтов выбирает самую последнюю версию в конфликтах, которые могут привести к совместимому набору зависимостей. Это означает, что в конце концов этот диспетчер конфликтов не допускает никаких конфликтов (например, диспетчер строгих конфликтов), за исключением того, что он следует стратегии наилучшего усилия, чтобы попытаться найти набор совместимых модулей (согласно ограничениям версии);
  • строгий : этот диспетчер конфликтов выдает исключение (т. Е. Вызывает сбой сборки) всякий раз, когда обнаруживается конфликт.

При необходимости вы можете предоставить собственный менеджер конфликтов .

5 голосов
/ 21 сентября 2009

Из перечисленных вами зависимостей следующие определены как optional в помете activemq-core (см. Также соответствующий раздел из книги Maven).

  • org.apache.derby: котелок
  • org.apache.geronimo.specs: Джеронимо-jta_1.0.1B_spec

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

В Maven необязательные зависимости обрабатываются автоматически. По сути, любая зависимость, которая объявлена ​​необязательной, должна быть повторно объявлена ​​в вашем pom, чтобы ее можно было использовать. Из документации, указанной выше:

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

Однако, поскольку проект не может быть разделен (опять же, по любой причине), эти зависимости объявляются необязательными. Если пользователь хочет использовать функциональность, связанную с необязательной зависимостью, он должен будет повторно объявить эту необязательную зависимость в своем собственном проекте. Это не самый очевидный способ справиться с этой ситуацией, но опять же и необязательные зависимости, и исключения из зависимостей являются решениями с ограничением.

Я не уверен, что вы можете настроить Ivy на игнорирование необязательных зависимостей, но вы можете настроить его на исключить зависимости . Например:

<dependency name="A" rev="1.0">
  <exclude module="B"/>
</dependency>

Это не совсем удовлетворительно, я знаю. Возможно, что Ivy поддерживает необязательные зависимости (я еще посмотрю и обновлю, если что-нибудь найду), но механизм исключений, по крайней мере, позволяет вам управлять ими.


Относительно последней части вашего вопроса. Maven разрешит версии зависимостей для log4j и, если версии совместимы, он автоматически выберет «ближайшую» из перечисленных версий.

Из Введение в механизм зависимости :

  • Посредничество зависимости - это определяет, какая версия зависимости будет использоваться при обнаружении нескольких версий артефакта. В настоящее время Maven 2.0 поддерживает только использование «ближайшего определения», что означает, что он будет использовать версию ближайшей зависимости к вашему проекту в дереве зависимостей. Вы всегда можете гарантировать версию, явно объявив ее в POM вашего проекта. Обратите внимание, что если две версии зависимостей находятся в одной и той же глубине в дереве зависимостей, до Maven 2.0.8 не было определено, какой из них выиграет, но начиная с Maven 2.0.9 учитывается порядок в объявлении: выигрывает первое объявление.

    • «ближайшее определение» означает, что используемая версия будет ближайшей к вашему проекту в дереве зависимостей, например. если зависимости для A, B и C определены как A -> B -> C -> D 2.0 и A -> E -> D 1.0, то D 1.0 будет использоваться при построении A, поскольку путь от A до D через Е короче. Вы можете явно добавить зависимость к D 2.0 в A, чтобы принудительно использовать D 2.0

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

5 голосов
/ 21 сентября 2009

Вот и все. Система зависимостей maven (за которой более или менее следует Айви) оставляет за отдельными проектами хорошую работу по добавлению необходимых метаданных для их зависимостей. Большинство не делают.

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

Плакатам, рекомендующим OSGi, ОП сказал, что он не желает реорганизовывать свою систему сборки для Maven, я не думаю, что он захочет реорганизовать свое приложение , чтобы оно было OSGi совместимый. Кроме того, многие проекты OSS, совместимые с OSGi (и их не так много, как можно было бы надеяться), имеют такие же плохие или худшие метаданные, как в Maven

4 голосов
/ 21 сентября 2009

Я думаю, что это действительно текущее положение дел. OSGi и предлагаемая новая система упаковки для Java 1.7 (уже обсуждался вопрос?) - это попытки исправить, по крайней мере, проблему, связанную с различными версиями библиотеки , но я не думаю, что они смогут исправить вашу проблему прямо сейчас.

3 голосов
/ 22 сентября 2009

В настоящее время я использую Ivy для управления более чем 120 OSS и проприетарными библиотеками для нескольких проектов (некоторые автономные, некоторые зависимые). В 2005 году (когда Айви еще работал в Jayasoft) я решил (или должен был) написать файлы ivy.xml для каждого интегрированного пакета.

Самым большим преимуществом является то, что я имею полный контроль над различными конфигурациями. Для некоторых это может показаться излишним, но наша система сборки надежно работает уже более 4 лет, и добавление новой библиотеки обычно занимает 5 минут.

2 голосов
/ 21 сентября 2009

«Это текущее состояние дел?»

Не с OSGi. Вы можете посмотреть на контейнеры и пакеты OSGi. Пакет подобен файлу JAR, но он поддерживает метаданные с подробным описанием своей версии и версий связанных пакетов, которые ему требуются (путем помещения атрибутов в файл META-INF). Таким образом, ваш комплект log4j будет указывать его версию, а зависимые комплекты будут указывать, какие версии log4j им требуются.

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

Javaworld имеет очень хорошее введение здесь .

0 голосов
/ 22 сентября 2009

Существует целая идея внедрения зависимости - которая неизбежно приведет к необходимости реструктуризации программы. Я слышал шум о том, что GUICE хорош в этом отношении. В плане развертывания я добился разумного успеха, развернув только .jar, который мы создали с зависимостями .jars, извлекаемыми из оригинальных проектов через jnlp. В основе системы сборки лежит ручное отслеживание новых версий зависимостей и обновление в системе сборки.

0 голосов
/ 21 сентября 2009

"Это текущее состояние дел"

Да.

Это компромисс с открытым исходным кодом.

Фреймворк с закрытым исходным кодом (т.е. .Net) решит все это за вас.

Решение с открытым исходным кодом означает, что вы должны решать его (и разрешать) постоянно.

Возможно, вам удастся найти некоторые предварительно созданные конфигурации и заплатить кому-то, чтобы они были в курсе. Например, вы можете использовать Red Hat Enterprise Linux. Если вы придерживаетесь именно того, что они поддерживают (и ничего более), то конфигурация решена.

Вероятность того, что ни одна упакованная конфигурация не соответствует вашим требованиям, хорошая.

...