Trovare dipendenze Gradle inutilizzate

1. Panoramica

A volte durante lo sviluppo, potremmo finire per aggiungere più dipendenze di quelle che usiamo.

In questo breve tutorial, vedremo come utilizzare il plug-in Gradle Nebula Lint per identificare e risolvere problemi come questi.

2. Installazione e configurazione

Usiamo una configurazione Gradle 5 multi-modulo nei nostri esempi.

Questo plugin funziona solo per i file di build basati su Groovy .

Configuriamolo nel file di build del progetto root:

plugins { id "nebula.lint" version "16.9.0" } description = "Gradle 5 root project" allprojects { apply plugin :"java" apply plugin :"nebula.lint" gradleLint { rules=['unused-dependency'] } group = "com.baeldung" version = "0.0.1" sourceCompatibility = "1.8" targetCompatibility = "1.8" repositories { jcenter() } }

Possiamo configurarlo in questo modo solo per build multiprogetto per il momento . Ciò significa che non possiamo applicarlo separatamente in ogni modulo.

Successivamente, configuriamo le nostre dipendenze del modulo:

description = "Gradle Unused Dependencies example" dependencies { implementation('com.google.guava:guava:29.0-jre') testImplementation('junit:junit:4.12') }

Ora aggiungiamo una semplice classe principale nei nostri sorgenti del modulo:

public class UnusedDependencies { public static void main(String[] args) { System.out.println("Hello world"); } }

Lo svilupperemo un po 'più tardi e vedremo come funziona il plugin.

3. Scenari e rapporti di rilevamento

Il plug-in cerca nei jar di output per rilevare se viene utilizzata o meno una dipendenza.

Tuttavia, a seconda di diverse condizioni, può darci risultati diversi .

Esploreremo i casi più interessanti nelle prossime sezioni.

3.1. Dipendenze inutilizzate

Ora che abbiamo la nostra configurazione, vediamo il caso d'uso di base. Siamo interessati alle dipendenze inutilizzate.

Cerchiamo di eseguire l' lintGradle compito:

$ ./gradlew lintGradle > Task :lintGradle FAILED # failure output omitted warning unused-dependency this dependency is unused and can be removed unused-dependencies/build.gradle:6 implementation('com.google.guava:guava:29.0-jre') ✖ 1 problem (0 errors, 1 warning) To apply fixes automatically, run fixGradleLint, review, and commit the changes. # some more failure output 

Vediamo cosa è successo. Abbiamo una dipendenza inutilizzata ( guava ) nella nostra configurazione compileClasspath .

Se noi corriamo fixGradleLint compito come suggerisce il plugin, la dipendenza viene automaticamente rimosso dal nostro build.gradle .

Tuttavia, usiamo invece una logica fittizia con la nostra dipendenza:

public static void main(String[] args) { System.out.println("Hello world"); useGuava(); } private static void useGuava() { List list = ImmutableList.of("Baledung", "is", "cool"); System.out.println(list.stream().collect(Collectors.joining(" "))); }

Se lo rieseguiamo non otteniamo più errori:

$ ./gradlew lintGradle BUILD SUCCESSFUL in 559ms 3 actionable tasks: 1 executed, 2 up-to-date

3.2. Utilizzo di dipendenze transitive

Includiamo ora un'altra dipendenza:

dependencies { implementation('com.google.guava:guava:29.0-jre') implementation('org.apache.httpcomponents:httpclient:4.5.12') testImplementation('junit:junit:4.12') }

Questa volta, usiamo qualcosa da una dipendenza transitiva:

public static void main(String[] args) { System.out.println("Hello world"); useGuava(); useHttpCore(); } // other methods private static void useHttpCore() { SSLContextBuilder.create(); }

Vediamo cosa succede:

$ ./gradlew lintGradle > Task :lintGradle FAILED # failure output omitted warning unused-dependency one or more classes in org.apache.httpcomponents:httpcore:4.4.13 are required by your code directly (no auto-fix available) warning unused-dependency this dependency is unused and can be removed unused-dependencies/build.gradle:8 implementation('org.apache.httpcomponents:httpclient:4.5.12') ✖ 2 problems (0 errors, 2 warnings)

Otteniamo due errori. Il primo errore dice approssimativamente che dovremmo fare riferimento direttamente a httpcore .

Il SSLContextBuilder nel nostro esempio ne fa effettivamente parte.

Il secondo errore dice che non stiamo usando nulla da httpclient.

Se usiamo una dipendenza transitiva, il plugin ci dice di renderla diretta .

Diamo uno sguardo al nostro albero delle dipendenze:

$ ./gradlew unused-dependencies:dependencies --configuration compileClasspath > Task :unused-dependencies:dependencies ------------------------------------------------------------ Project :unused-dependencies - Gradle Unused Dependencies example ------------------------------------------------------------ compileClasspath - Compile classpath for source set 'main'. +--- com.google.guava:guava:29.0-jre | +--- com.google.guava:failureaccess:1.0.1 | +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava | +--- com.google.code.findbugs:jsr305:3.0.2 | +--- org.checkerframework:checker-qual:2.11.1 | +--- com.google.errorprone:error_prone_annotations:2.3.4 | \--- com.google.j2objc:j2objc-annotations:1.3 \--- org.apache.httpcomponents:httpclient:4.5.12 +--- org.apache.httpcomponents:httpcore:4.4.13 +--- commons-logging:commons-logging:1.2 \--- commons-codec:commons-codec:1.11

In questo caso, possiamo vedere che httpcore è portato da httpclient .

3.3. Utilizzo delle dipendenze con la riflessione

E quando usiamo la riflessione?

Miglioriamo un po 'il nostro esempio:

public static void main(String[] args) { System.out.println("Hello world"); useGuava(); useHttpCore(); useHttpClientWithReflection(); } // other methods private static void useHttpClientWithReflection() { try { Class httpBuilder = Class.forName("org.apache.http.impl.client.HttpClientBuilder"); Method create = httpBuilder.getMethod("create", null); create.invoke(httpBuilder, null); } catch (Exception e) { e.printStackTrace(); } }

Ora eseguiamo nuovamente l'attività Gradle:

$ ./gradlew lintGradle > Task :lintGradle FAILED # failure output omitted warning unused-dependency one or more classes in org.apache.httpcomponents:httpcore:4.4.13 are required by your code directly (no auto-fix available) warning unused-dependency this dependency is unused and can be removed unused-dependencies/build.gradle:9 implementation('org.apache.httpcomponents:httpclient:4.5.12') ✖ 2 problems (0 errors, 2 warnings) 

Quello che è successo? Abbiamo utilizzato HttpClientBuilder dalla nostra dipendenza (httpclient) ma abbiamo comunque ricevuto errori.

Se usiamo una libreria con reflection, il plugin non ne rileva l'utilizzo .

Di conseguenza, possiamo vedere gli stessi due errori.

In generale, dovremmo configurare tali dipendenze come runtimeOnly .

3.4. Generazione di rapporti

Per i grandi progetti, il numero di errori restituiti in un terminale diventa difficile da gestire.

Configuriamo invece il plugin per darci un rapporto:

allprojects { apply plugin :"java" apply plugin :"nebula.lint" gradleLint { rules=['unused-dependency'] reportFormat = 'text' } // other details omitted }

Eseguiamo l' attività generateGradleLintReport e controlliamo il nostro output di compilazione:

$ ./gradlew generateGradleLintReport # task output omitted $ cat unused-dependencies/build/reports/gradleLint/unused-dependencies.txt CodeNarc Report - Jun 20, 2020, 3:25:28 PM Summary: TotalFiles=1 FilesWithViolations=1 P1=0 P2=3 P3=0 File: /home/user/tutorials/gradle-5/unused-dependencies/build.gradle Violation: Rule=unused-dependency P=2 Line=null Msg=[one or more classes in org.apache.httpcomponents:httpcore:4.4.13 are required by your code directly] Violation: Rule=unused-dependency P=2 Line=9 Msg=[this dependency is unused and can be removed] Src=[implementation('org.apache.httpcomponents:httpclient:4.5.12')] Violation: Rule=unused-dependency P=2 Line=17 Msg=[this dependency is unused and can be removed] Src=[testImplementation('junit:junit:4.12')] [CodeNarc (//www.codenarc.org) v0.25.2] 

Ora rileva le dipendenze inutilizzate sulla configurazione testCompileClasspath .

Questo è, sfortunatamente, un comportamento incoerente del plugin. Di conseguenza, ora otteniamo tre errori.

4. Conclusione

In questo tutorial, abbiamo visto come trovare dipendenze inutilizzate su build Gradle.

Per prima cosa, abbiamo spiegato la configurazione generale. Successivamente, abbiamo esplorato gli errori segnalati con diverse dipendenze e il loro utilizzo.

Infine, abbiamo visto come generare report basati su testo.

Come al solito, possiamo trovare gli esempi di codice completi su GitHub.