5

Java 8 здесь.Может ли Gradle помочь решить jar ад в любом случае?

Say есть старая версия widget libray, с Maven координат widgetmakers:widget:1.0.4, что есть класс, определенный в нем, как так:

public class Widget { 
    private String meow; 

    // constructor, getters, setters, etc. 
} 

проходят года. Составители этой библиотеки widget решают, что Widget никогда не должен meow, скорее, это должно быть на самом деле bark. И поэтому новый релиз сделан с Maven координатами widgetmakers:widget:2.0.0 и Widget выглядит как:

public class Widget { 
    private Bark bark; 

    // constructor, getters, setters, etc. 
} 

Так что теперь я иду построить мое приложение, myapp. И, желая использовать последние стабильные версии всех мои зависимостей, я объявляю мою зависимость, как так (внутри build.gradle):

dependencies { 
    compile (
     ,'org.slf4j:slf4j-api:1.7.20' 
     ,'org.slf4j:slf4j-simple:1.7.20' 
     ,'bupo:fizzbuzz:3.7.14' 
     ,'commons-cli:commons-cli:1.2' 
     ,'widgetmakers:widget:2.0.0' 
    ) 
} 

Теперь давайте скажем, что это (вымышленная) fizzbuzz библиотеки имеет всегда зависело от 1.x библиотека widget, где Widget будет meow.

Так что теперь, я указание 2 версии widget на моей компиляции: пути к классам

  1. widgetmakers:widget:1.0.4 который втягивается в fizzbuzz библиотеки, как зависимость от него; и
  2. widgetmakers:widget:2.0.0 который я ссылка непосредственно

Так, очевидно, в зависимости от того, какой версии Widget получает classloaded первого, мы будем иметь либо Widget#meow или Widget#bark.

Есть ли в Gradle какие-либо средства для того, чтобы помочь мне здесь? Есть ли способ вытащить несколько версий одного и того же класса и настроить классы fizzbuzz для использования старой версии Widget, а также моих классов для использования новой версии? Если нет, то только решения я могу думать о том, являются:

  1. я мог бы быть в состоянии выполнить какое-то shading- и/или FatJar основе soltuion, где, возможно, я тянуть во всех своих зависимостей в виде пакетов под myapp/bin и затем дайте им разные префиксы версий. По общему признанию, я не вижу здесь ясного решения, но я уверен, что что-то возможно (но все же хакерское/противное). Или ...
  2. Осмотрите весь график зависимостей и просто убедитесь, что все мои транзитивные зависимости не конфликтуют друг с другом. В этом случае для меня это означает либо отправку запроса на тягу к сопровождающим fizzbuzz, чтобы обновить его до последней версии widget, либо, к сожалению, понизить рейтинг myapp, чтобы использовать старшую версию widget.

Но Gradle (до сих пор) был для меня волшебным. Поэтому я спрашиваю: есть ли какая-нибудь магия Грейдла, которая может помочь мне здесь?

+0

Единственный возможный способ, по которому я вижу, что вы можете правильно использовать две разные версии одной и той же библиотеки, - это перечислить каждую версию, где бы вы ни были сами или библиотекой. Если нет, тогда вам придется зависеть от версии Gradle. Также вы получите исключение класса/метода, не найденное, если это неправильная версия. Gradle может найти зависимости, которые отсутствуют и вытащить их, но вручную саботируя механизм разрешения зависимостей Gradle, запутав его с помощью двух библиотек, в конечном итоге не принесет пользы. –

+0

Спасибо @WeareBorg (+1). ** (1) ** Когда вы говорите «* ссылаясь на каждую версию, где бы вы ни были сами или в библиотеке *», можете ли вы уточнить немного больше? Я был разработчиком JVM более 10 лет и не знаком с этой стратегией/подходом! Можете ли вы привести пример того, что вы имеете в виду? И ** (2) ** что такое поведение Gradle по умолчанию в этой ситуации? Оставленный нетронутым, какая версия библиотеки 'widget' будет Gradle в конечном итоге получить/решить? Еще раз спасибо! – smeeb

+1

1) Я имел в виду что-то простое, но я знаю синтаксис для maven, где вы можете исключить библиотеку, в которой вам не понадобится определенная версия, поэтому другая версия автоматически вытягивается.2) Для градиента из-за добавлений в classpath он будет первым в списке, для maven он будет последним. –

ответ

5

Не знаю специфики Gradle, так как я человек Maven, но в любом случае это более общий. У вас в основном есть два варианта (и оба они хаки):

  1. ClassLoader magic. Как-то вам нужно убедить вашу систему сборки загрузить две версии библиотеки (удачи с этим), а затем во время выполнения загрузите классы, которые используют старую версию, с ClassLoader, у которого есть старая версия. Я сделал это, но это боль. (Инструменты, такие как OSGI, могут отнять часть этой боли)
  2. Пакет затенения. Повторно упакуйте библиотеку A, которая использует старую версию библиотеки B, так что B фактически находится внутри A, но с префиксом пакета B. Это обычная практика, например. Весенние суда its own version of asm. На стороне Maven maven-shade-plugin делает это, вероятно, эквивалент Gradle. Или вы можете использовать ProGuard, 800-фунтовый горилл манипуляции Jar.
+0

Спустившись по всем этим дорожкам раньше, я бы определенно попробовал подход тени, если это не сработает, OSGI, а затем, вероятно, оставит ручную мануальную загрузку класса в качестве последнего средства. – biziclop

+0

@biziclop да я согласен, что это наименее болезненный способ –

+0

Кажется, лучший ответ на липкую проблему. – cjstehno

2

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

EDIT: Бьорн прав в том, что он попытается разрешить конфликты в разных версиях; он будет компилировать путь к классам на основе его стратегий, поэтому порядок, в который вы помещаете свои зависимости в файл, не имеет значения. Однако вы по-прежнему получаете только один класс для каждого класса, он не решит проблему OP

+1

Это неправильно. Gradle разрешит конфликт на основе выбранной стратегии разрешения конфликтов. По умолчанию он будет использовать самую новую версию всей запрошенной версии, если вы не настроите Gradle по-другому. E. г. ошибки или использовать определенную фиксированную версию или что-то в этом роде. Он не добавит обе версии в путь к классам, если только координаты равны, кроме версии. – Vampire

0

Если у вас разные версии библиотеки с одинаковыми координатами, вступает в действие механизм разрешения конфликтов Gradles.

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

Если вам действительно нужны разные версии одной и той же библиотеки во время выполнения, вам придется либо сделать магию ClassLoader, которая определенно возможна, либо сделать некоторое затенение для одной из библиотек или того и другого.

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

0

Хуже того, когда один и тот же класс появляется в нескольких баночках. Это более коварно - посмотрите на метрики из Codahale и Dropwizard с несовместимыми версиями одного и того же класса в двух баночках. Путь класса градиента - адский плагин может обнаружить этот ужас.

Смежные вопросы