Правильно или неправильно для Java JAR содержать свои собственные зависимости?

Я думаю, это вопрос из двух частей. Я пытаюсь написать свою собственную задачу Ant (MyFirstTask), которую можно использовать в файлах сборки build.xml других проектов. Для этого мне нужно скомпилировать и упаковать мою задачу Ant в собственный JAR-файл. Поскольку эта задача Ant, которую я написал, довольно сложна, она имеет около 20 зависимостей (других файлов JAR), таких как использование XStream для OX-mapping, Guice для DI и т. д.

В настоящее время я пишу задачу package в файле build.xml внутри проекта MyFirstTask (файл сборки, который будет упаковывать myfirsttask.jar, который является многократно используемой задачей Ant).

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

Или это намерение, чтобы JAR-файлы Java также включали свои зависимости?

В любом случае, я не хочу заставлять своих пользователей копировать и вставлять более 25 JAR-файлов в свои библиотеки Ant; это просто жестоко. Мне нравится способ настройки файлов WAR, где путь к классам для зависимостей определяется в каталоге classes/.

Думаю, в конечном итоге я бы хотел, чтобы моя структура JAR выглядела так:

myfirsttask.jar/
    com/  --> the root package of my compiled binaries
    config/  --> config files, XML, XSD, etc.
    classes/  --> all dependencies, guice-3.0.jar, xstream-1.4.3.jar, etc.
    META-INF/
        MANIFEST.MF

Я предполагаю, что для того, чтобы выполнить это (и заставить путь к классам среды выполнения также заглядывать в каталог classes/), мне нужно каким-то образом изменить MANIFEST.MF (я знаю, что есть атрибут манифеста с именем ClassPath , Я считаю?). Мне просто трудно собрать все вместе, и у меня есть вырисовывающийся / затянувшийся вопрос о самом назначении JAR с самого начала.

Может ли кто-нибудь подтвердить, намерен ли Oracle, чтобы JAR-файлы содержали свои зависимости или нет? И в любом случае, что мне нужно сделать в манифесте (или где-либо еще), чтобы убедиться, что во время выполнения путь к классам может найти зависимости, хранящиеся в каталоге classes/? Заранее спасибо!


person IAmYourFaja    schedule 30.09.2012    source источник
comment
Деталь, которую вы, вероятно, здесь упускаете, заключается в том, предназначено ли это для контейнера EE или автономного приложения. На этот вопрос нет правильного ответа, но в контейнерах есть определенные неправильные ответы. Банки могут содержать зависимости, так что да, это сделано намеренно. Это хороший дизайн? Не всегда.   -  person Daniel B. Chapman    schedule 30.09.2012
comment
Полный пример использования ivy для удовлетворения зависимостей может быть полезен. См. этот ответ, который подходит для задачи script.   -  person Jarekczek    schedule 30.09.2012


Ответы (5)


Термин «файл JAR» может означать как минимум две вещи или, скорее, имеет как минимум два аспекта своего значения. В основном это означает формат контейнера: в основном, файл ZIP с каталогом META-INF. Точнее, это означает, что этот контейнер используется как способ упаковки файлов классов.

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

Если вы читали спецификацию файла JAR вы обнаружите несколько упоминаний о хранении файлов классов, но ничего о хранении других файлов JAR. Соответственно, если вы посмотрите на реализацию загрузчика классов JAR-файлов в JRE, вы не сможете сделать ничего полезного с вложенными JAR-файлами.

Кроме того, спецификация JAR подробно описывает механизм работы с невложенными зависимостями: атрибут Class-Path. Это позволяет файлу JAR делать относительные ссылки на другие файлы JAR в файловой системе.

Теперь JAR-файлы в смысле упаковки — это не единственное использование JAR-файлов в смысле контейнера. Файлы WAR, EAR и RAR (и многое другое) — это файлы JAR, используемые для определенных целей. Каждый из этих может содержать другие JAR-файлы: WAR-файлы могут содержать JAR-файлы в смысле упаковки, а EAR-файлы могут содержать их, а также WAR-файлы. Однако это совсем другие звери, чем JAR-файлы в смысле упаковки. Стоит отметить, что для их использования необходимы специальные загрузчики классов, которых нет в стандартной библиотеке Java.

Способ, которым WAR и т. д. могут собирать множество JAR-файлов вместе, действительно очень полезен, и очень жаль, что нет общего механизма для этого в Java за пределами Java EE. Было бы здорово иметь формат «архива приложений» или «метаархива», который просто объединяет несколько JAR-файлов.

Итак, у вас осталась эта проблема: пользователям нужно 25 JAR-файлов, чтобы использовать ваш плагин. У вас есть примерно два варианта.

Во-первых, вы принимаете боль и распространяете свой плагин в виде zip-файла, полного JAR-файлов, которые пользователям придется распаковывать.

Во-вторых, вы присоединяетесь к 21 веку и используете инструмент сборки и механизм распределения, который автоматически обрабатывает зависимости: на практике это означает использование Gradle, Maven или какого-либо другого инструмента (например, Ant) совместно с Ivy для получения зависимостей от Maven Central, а затем выпустить свой код вместе с файлом POM, в котором перечислены эти зависимости. Затем пользователи могут загрузить ваш JAR и ваш POM, а также использовать собственный инструмент сборки для получения зависимостей.

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

person Tom Anderson    schedule 30.09.2012

Намерение (AFAIU) состоит в том, чтобы файлы JAR вели себя как общие объектные файлы собственного кода (.so в Unix, .dll в Windows). Как правило, приложение устанавливает несколько общих объектных файлов как одноуровневых, а также исполняемый файл для их запуска.

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

К сожалению, ClassLoader по умолчанию не может загружать классы из вложенных JAR-файлов. Можно написать ClassLoader, который это делает. Или вы можете использовать тот, который написал кто-то другой. Судя по описанию вашей проблемы, Jar Jar Links — это именно то, что вы ищете. за.

person Daniel Pryden    schedule 30.09.2012

Правильно или неправильно, чтобы JAR-файл Java содержал собственные зависимости?

Есть случаи использования, когда для файла JAR правильно содержать свои собственные зависимости. Если вы хотите поддержать пользователей, которые не используют современное управление зависимостями, вы можете предоставить файл JAR, содержащий код вашей задачи Ant, а также все зависимости. Более мощный, гибкий и модульный подход заключается в публикации файлов JAR с версиями в репозиторий Maven, который содержит только код вашего проекта.

1) JAR-файл, содержащий код вашего проекта и все зависимости

Плюсы

  • Легко загрузить, и единственная настройка для конечных пользователей — это включение <taskdef> в их Ant файлы сборки
  • Для публикации артефактов Maven настройка не требуется.

Пример цели Ant для создания JAR

<target name="jar" depends="compile"
    description="Creates a standalone JAR of all class files and dependencies.">
  <jar destfile="${my.ant.task.jar.file}" update="true">
    <fileset dir="${build.classes.dir}" />
    <zipfileset src="${lib.dir}/javax.inject.jar" />
    <zipfileset src="${lib.dir}/guice-3.0.jar" />
    <zipfileset src="${lib.dir}/guice-multibindings-3.0.jar" />
    <zipfileset src="${lib.dir}/guice-assistedinject-3.0.jar" />
  </jar>
</target>

Минусы

  • Если у конечных пользователей вашей задачи Ant уже есть некоторые или все зависимости, включенные в их проекты, они получат избыточные копии зависимостей.
  • Файл JAR может быть очень большим

2) Файл JAR, содержащий только код вашего проекта, опубликованный в репозитории Maven.

Плюсы

  • Пользователи могут загрузить любую опубликованную вами версию задачи Ant в репозиторий Maven, что обеспечивает большую гибкость при выпуске новых версий. вашей задачи, позволяя существующим пользователям продолжать использовать предыдущие версии, чтобы избежать возможных регрессий
  • Избегает дублирования копий общих зависимостей (за исключением случаев, когда разные версии зависимости вызывают ошибки)
  • JAR-файл будет маленьким

Минусы

Необходимо узнать о следующем:


Для справки, учебники по Java содержат хороший обзор файлов JAR.

Урок: Упаковка программ в файлы JAR

Формат файлов архива Java (JAR) позволяет объединять несколько файлов в один файл архива. Обычно файл JAR содержит файлы классов и вспомогательные ресурсы, связанные с... приложениями.

Формат файла JAR предоставляет множество преимуществ:

  • Безопасность: содержимое файла JAR можно подписать цифровой подписью...
  • Уменьшено время загрузки: если ваш апплет упакован в JAR...
  • Сжатие. Формат JAR позволяет сжимать файлы для эффективного хранения.
  • Упаковка для расширений. Платформа расширений предоставляет средства, с помощью которых вы можете добавлять функциональные возможности к базовой платформе Java, а формат файла JAR определяет упаковку для расширений...
  • Запечатывание пакетов: пакеты, хранящиеся в файлах JAR, могут быть дополнительно запечатаны, чтобы пакет мог обеспечить согласованность версий. Запечатывание пакета в файле JAR означает, что все классы, определенные в этом пакете, должны находиться в одном и том же файле JAR.
  • Управление версиями пакетов. Файл JAR может содержать данные о содержащихся в нем файлах, например сведения о поставщике и версии.
  • Переносимость. Механизм обработки файлов JAR является стандартной частью основного API платформы Java.
person Christopher Peisert    schedule 30.09.2012

«Jar Jar Links» хорош только для автономных приложений. Но не для Анта. Если в вашем проекте есть такие же зависимости, и они позже будут обновлены до более новых версий, например, xstream-*.jar, тогда возникнет конфликт, и может быть выбрана неправильная версия. В худшем случае будет MethodNotFoundException. Вот почему включать зависимости в одну банку — плохая практика.

В чем проблема с «Я не хочу заставлять своих пользователей копировать и вставлять более 25 JAR-файлов»?

Это самое простое решение. И самое лучшее, потому что вы избежите проблем в будущем.

Теперь, когда вы видите неудобства Ant, вы можете сравнить его с Gradle. С Gradle вы получаете задачи, немного похожие на Ant, и вам не нужно предоставлять какие-либо jar-файлы зависимостей. Все зависимости для вас разрешит Gradle. И, как и в Ant, вы все еще можете создавать свои задачи.

person Mentallurg    schedule 30.09.2012

Некоторые поставщики Java-приложений используют следующий сценарий для распространения своего приложения, которое зависит от других jar-файлов, это напоминает статическую компоновку. На этапе сборки jar все зависимости (также являющиеся jar) распаковываются. При сборке окончательного jar-файла они включают как свои только что скомпилированные классы, так и классы, извлеченные из зависимостей.

Возможные проблемы:

  1. Приложения не могут повторно использовать библиотеки, поскольку они содержатся в приложении. Обычная проблема статического связывания.
  2. Лицензии перепакованных библиотек должны соблюдаться. Обычно их можно переупаковать, но иногда необходимо уделить особое внимание их лицензионным файлам, которые могут оказаться внутри их банок.

AFAIK невозможно иметь банки внутри банки, или для них будет невозможно указать путь к классам. Отсюда и процедура перепаковки.

person Jarekczek    schedule 30.09.2012