Pocho­pi­telně není možné znát do hloubky všechny nástroje a frameworky, se kterými denně přicházíme do styku. Na druhou stranu, pragma­tický programátor se snaží pochopit alespoň principy, obzvláště u klíčových techno­lo­gií. Tentokrát bych chtěl vysvětlit konflikt tranzi­tiv­ních závis­lostí v Mavenu (ukážu i alter­na­tivu v Gradlu).

Zadání

Máme projekt s hromadou závis­lostí a ty s sebou nesou další tranzi­tivní závis­losti. Ale co se stane, zavleč­e­me-li si do projektu stejný artefakt jiné verze?

Maven

Co na to Maven? Projekt možná nakrásně zkompi­lu­jete, ale za běhu se můžou dít věci… najednou na vás vyskočí ClassNotFoundException či NoSuchMethodError, ačkoliv tam patřičnou třídu přeci máte, ne? Nejspíš máte, ale třeba binárně nekom­pa­ti­bilní. Problém je v tom, že při konfliktu tranzi­tiv­ních závis­lostí Maven nekřičí, ani nevezme nejvyšší verzi (jak byste nejspíš čekali), ale zvolí tu nejbližší ve stromě závis­lostí. Více v dokumen­taci Intro­duc­tion to the Depen­dency Mechanism.

Pojďme si to ukázat na násle­dujícím příkladu. Jistě, v takto jedno­duchém případě byste měli vybrat stejné verze Springu, ale v kompli­ko­vanějším projektu se vám závis­losti zavlečou, ani nevíte odkud.

Nechme si vypsat seznam závis­lostí pomocí příkazu mvn dependency:list

Hm, vidíme sprin­g-aop 3.0.7, zkont­ro­lujme "proč" a to pomocí příkazu mvn depen­den­cy:tree -Dverbose

Teď, když už víme proč, můžeme danou závis­lost exludo­vat, případně expli­citně defino­vat, čímž verzi přebijeme.

Všimněte si, že spring-web se vybral ve vyšší verzi, ovšem nikoliv proto, že je novější, ani proto, že je v tranzi­tivní závis­losti blíž. V tranzi­tivní závis­losti je ve stejné vzdále­nosti, ale je v pomu defino­vaný dříve.

Gradle

Koluje takový vtip:

Jaký je rozdíl mezi autory Mavenu a Antu? Autoři Antu už se omluvili.

A co na to Gradle? Mějme ekviva­lentní konfi­gu­raci předchozímu maven příkladu.

Nechme si vypsat závis­losti pomocí příkazu gradle dependencies

Všimněte si, že jsou všude použity nejvyšší verze, což je výchozí chování Gradlu. Můžete být ovšem přísnější a v případě konfliktu nechat build selhat, čehož docílíte použitím failOnVersionConflict() (odko­men­tujte TODO ve skriptu výše).

V tom případě musíte verzi vynutit pomocí force. Více v dokumen­taci Resolve version conflicts a v javadocu ResolutionStrategy.

Související