Menemukan Dependensi Gradle yang Tidak Digunakan

1. Ikhtisar

Terkadang selama pengembangan, kami mungkin akan menambahkan lebih banyak dependensi daripada yang kami gunakan.

Dalam tutorial singkat ini, kita akan melihat cara menggunakan plugin Gradle Nebula Lint untuk mengidentifikasi dan memperbaiki masalah seperti ini.

2. Setup dan Konfigurasi

Kami menggunakan penyiapan Gradle 5 multi-modul dalam contoh kami.

Plugin ini hanya berfungsi untuk file build berbasis Groovy .

Mari kita konfigurasikan di file build proyek 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() } }

Kami hanya dapat mengonfigurasinya dengan cara ini untuk build multi-project untuk saat ini . Artinya, kami tidak dapat menerapkannya secara terpisah di setiap modul.

Selanjutnya, mari konfigurasikan dependensi modul kita:

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

Sekarang mari tambahkan kelas utama sederhana di sumber modul kita:

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

Kami akan mengembangkannya nanti dan melihat cara kerja plugin.

3. Skenario dan Laporan Deteksi

Plugin mencari keluaran untuk mendeteksi apakah sebuah ketergantungan digunakan atau tidak.

Namun, bergantung pada beberapa kondisi, ini dapat memberi kita hasil yang berbeda .

Kami akan menjelajahi kasus yang lebih menarik di bagian selanjutnya.

3.1. Dependensi yang tidak terpakai

Sekarang setelah kita memiliki pengaturan kita, mari kita lihat kasus penggunaan dasar. Kami tertarik pada dependensi yang tidak digunakan.

Mari jalankan tugas lintGradle :

$ ./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 

Mari kita lihat apa yang terjadi. Kami memiliki dependensi yang tidak digunakan ( guava ) dalam konfigurasi compileClasspath kami .

Jika kita menjalankan tugas fixGradleLint seperti yang disarankan plugin, dependensi secara otomatis dihapus dari build.gradle kita .

Namun, mari gunakan beberapa logika dummy dengan ketergantungan kita sebagai gantinya:

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(" "))); }

Jika kami menjalankannya kembali, kami tidak mendapatkan lagi kesalahan:

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

3.2. Menggunakan Ketergantungan Transitif

Sekarang mari kita sertakan ketergantungan lain:

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

Kali ini, mari gunakan sesuatu dari ketergantungan transitif:

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

Mari lihat apa yang terjadi:

$ ./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)

Kami mendapatkan dua kesalahan. Kesalahan pertama secara kasar mengatakan kita harus mereferensikan httpcore secara langsung.

The SSLContextBuilder dalam sampel kami sebenarnya adalah bagian dari itu.

Kesalahan kedua mengatakan kami tidak menggunakan apa pun dari httpclient.

Jika kita menggunakan ketergantungan transitif, plugin memberitahu kita untuk membuatnya menjadi ketergantungan langsung .

Mari kita intip pohon ketergantungan kita:

$ ./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

Dalam kasus ini, kita dapat melihat bahwa httpcore dibawa oleh httpclient .

3.3. Using Dependencies with Reflection

What about when we use reflection?

Let's enhance our example a bit:

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(); } }

Now let's rerun the Gradle task:

$ ./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) 

What happened? We used HttpClientBuilder from our dependency (httpclient) but still got errors.

If we use a library with reflection, the plugin does not detect its usage.

As a result, we can see the same two errors.

In general, we should configure such dependencies as runtimeOnly.

3.4. Generating Reports

For big projects, the number of errors returned in a terminal becomes challenging to handle.

Let's configure the plugin to give us a report instead:

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

Let's run the generateGradleLintReport task and check our build output:

$ ./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] 

Sekarang mendeteksi dependensi yang tidak digunakan pada konfigurasi testCompileClasspath .

Sayangnya, ini adalah perilaku plugin yang tidak konsisten. Akibatnya, kami sekarang mendapatkan tiga kesalahan.

4. Kesimpulan

Dalam tutorial ini, kami melihat cara menemukan dependensi yang tidak digunakan pada build Gradle.

Pertama, kami menjelaskan pengaturan umum. Setelah itu, kami menjelajahi kesalahan yang dilaporkan dengan dependensi berbeda dan penggunaannya.

Akhirnya, kami melihat bagaimana membuat laporan berbasis teks.

Seperti biasa, kami dapat menemukan contoh kode lengkap di GitHub.