Liigu peamise sisu juurde

Sõltuvuste haldamine

Sissejuhatus

Ühe ainsa teegi projekti lisamine on lihtne ja otsekohene. Keerukus tekib siis, kui sellel teegil on omad sõltuvused ja neil sõltuvustel omakorda järgmised sõltuvused. Sellist ahelat nimetatakse transitiivseteks sõltuvusteks ja just siit tulevad enamik reaalseid ehitusprobleeme.

Nii Maven kui ka Gradle käsitlevad transitiivseid sõltuvusi automaatselt, kuid lahendavad nendest tulenevaid versioonikonflikte erinevalt. Nende tööpõhimõtete mõistmine on tähtis olukorras, kus projekti ehitusprotsess läheb katki kahe erineva sõltuvuse versiooni erinevuse pärast.

Transitiivsed sõltuvused

Oletame, et projekt sõltub teegist A ja teek A sõltub teegist B. Konfiguratsioonis on deklareeritud ainult teek A, kuid B tõmmatakse automaatselt kaasa:

Your Project
└── A (You declared this dependency)
└── B (Automatically downloaded along side A)

See on mugav kuna ei pea käsitsi jälgima, millest sõltuvused sõltuvad. Kuid see tähendab ka seda, et sinu classpath sisaldab teeke, mida sa otseselt ei deklareerinud.

Kõiki alla laetud teeke saab vaadata järgnevalt:

# Maven
mvn dependency:tree

# Gradle
./gradlew dependencies

Need käsud näitavad igat teeki, milline sõltuvus selle kaasa tõi ja valitud versiooni. See on esimene tööriist, mille poole pöörduda, kui midagi läheb valesti.

Versioonikonfiktid

Konflikt tekib siis, kui kaks sõltuvust vajavad sama teegi erinevaid versioone. Näiteks:

Your Project
├── gson:2.10.1
└── jackson-databind:2.9.0
└── gson:2.8.6 (transitive)

Projektis on deklareeritud Gson versioon 2.10.1, kuid Jackson (oma sõltuvuste kaudu) kasutab versiooni 2.8.6. Mõlemad versioonid ei saa classpath'is koos eksisteerida ehk ehitusinstrument peab valima ühe nendest.

Kuidas Maven konflikte lahendab

Maven kasutab neartest-fist lahendust ehk valitakse see versioon, mis on sõltuvuspuu juurele kõige lähemal deklareeritud.

Ülaltoodud näites on otsene Gsoni 2.10.1 deklaratsioon sügavusel 1. Transitiivne 2.8.6 on sügavusel 2. Maven valib 2.10.1, sest see on juurele lähemal.

Kui kaks transitiivset sõltuvust samal sügavusel vajavad erinevaid versioone, valib Maven selle, mis on POM-is esimesena deklareeritud.

Kuidas Gradle konflikte lahendab

Gradle kasutab vaikimisi newest-wins lahendust ehk Gradle valib antud teegi kõrgeima versiooninumbri.

Samas näites valiks Gradle 2.10.1, kuna see on uuem kui 2.8.6.

hoiatus

Kumbki strateegia pole 100% ohutu. Uuemal versioonil võivad olla fundamentaalsed muudatused, mis rikuvad varasemat kasutuskogemust. Vanemal versioonil võivad puududa funktsioonid, mida sellest sõltuv teek vajab. Kontrolli alati oma sõltuvuspuud pärast uute teekide lisamist.

Versiooni sundimine

Nii Maven kui ka Gradle pakuvad viisi, kuidas sundida mõni teek kindlale versioonile. See tagab stabiilsust üle terve projekti.

Maven: dependencyManagement

<dependencyManagement> plokk seab versioonireeglid, mis kehtivad kõikidele sõltuvustele, nii otsestele kui ka transitiivsetele:

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
</dependencyManagement>
hoiatus

See ei lisa Gsoni sõltuvusena, vaid määrab tervet projekti kasutama versiooni 2.10.1. Sõltuvusena lisamiseks vajad ikkagi eraldi <dependency> kirjet <dependencies> plokis.

<!-- These two blocks are not the same-->

<!--- Adds Gson as a dependency --->
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>

<!--- Locks down Gson version to 2.10.1 --->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
</dependencyManagement>

Gradle: resolutionStrategy

Gradle'i vaste sellele on resolutionStrategy configurations.all ploki sees:

configurations.all {
resolutionStrategy {
force 'com.google.code.gson:gson:2.10.1'
}
}

See tühistab iga Gsoni versiooni, mis sõltuvuspuus esineb, asendades selle versiooniga 2.10.1.

Transitiivse sõltuvuse välistamine

Mõnikord ei ole probleem mitte versioonis, vaid sõltuvuses endas. Teek võib kaasa tõmmata midagi, mida sa ei soovi. On olukordi, kus antud teek võib olla vastuolus teise teegiga või on lihtsalt ebavajalik.

Maven: exclusion

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>

Gradle: exclude

implementation('com.fasterxml.jackson.core:jackson-databind:2.9.0') {
exclude group: 'com.google.code.gson', module: 'gson'
}

Mõlemad takistavad Gsoni kaasatõmbamist Jacksoni kaudu, olenemata versioonist.

Millal sundida vs millal välistada

OlukordLähenemine
Kaks sõltuvust vajavad sama teegi erinevaid versiooneMäära projekti jaoks versioon, mis töötab mõlema jaoks
Transitiivne sõltuvus on vastuolus sinu deklareeritud otsese sõltuvusegaMäära enda poolt deklareeritud versioon
Transitiivne sõltuvus on täiesti ebavajalik või põhjustab classpath-i probleemeVälista see
Soovid standardiseerida teegi versiooni suures projektisKasuta dependencyManagement (Maven) või resolutionStrategy (Gradle)

Sõltuvuspuu käsk on sinu diagnostikatööriist kõigil juhtudel. Käivita see enne ja pärast muudatuste tegemist, et tulemust kontrollida.