Deklarasi dependensi di Maven berbeda dengan Gradle. Maven memakai scope untuk menentukan ketersediaan dependensi di berbagai phase build. Pemilihan scope yang tepat memengaruhi ukuran JAR akhir dan batas dependensi transitif yang ditarik. Pada chapter ini kita akan membahas empat scope utama Maven, cara deklarasi dependensi di pom.xml, serta perintah pemeriksaan dependency:tree dan dependency:analyze.

Empat Scope Utama #

ScopeSaat compileSaat testSaat runtimeTransitif
compile (default)YaYaYaYa, dengan scope compile
testTidakYaTidakYa, untuk scope test
providedYaYaTidakTidak transitif
runtimeTidakYaYaYa, dengan scope runtime

Mari kita bedah kapan memakai masing-masing scope:

  1. compile (default): dependensi yang dipakai di kode produksi dan runtime. Mis. kotlinx-coroutines-core.
  2. test: dependensi hanya untuk unit test. Mis. junit-jupiter, mockk. Tidak disertakan di JAR akhir.
  3. provided: dependensi yang disediakan oleh environment runtime. Mis. servlet-api di Tomcat, atau slf4j-api bila kita publikasikan library dan konsumer yang menyediakan implementasi.
  4. runtime: dependensi yang hanya dibutuhkan saat runtime, bukan saat compile. Mis. driver JDBC (org.postgresql:postgresql) yang dipakai via interface java.sql.Driver.

Deklarasi di pom.xml #

xml pom.xml — dependencies
<dependencies>
    <!-- Scope compile (default) -->
    <dependency>
        <groupId>org.jetbrains.kotlinx</groupId>
        <artifactId>kotlinx-coroutines-core</artifactId>
        <version>1.8.1</version>
    </dependency>

    <!-- Scope provided: API logging di library publik -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.13</version>
        <scope>provided</scope>
    </dependency>

    <!-- Scope runtime: driver JDBC -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.7.3</version>
        <scope>runtime</scope>
    </dependency>

    <!-- Scope test -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Demonstrasi Dependensi Maven #

Kode berikut mencantumkan empat scope utama dan perintah pemeriksaan.

kotlin
fun main() {
    println("=== Dependensi Maven ===")
    println()
    println("Empat scope utama:")
    println("  compile (default) - tersedia di compile + runtime + transitif")
    println("  test               - hanya untuk test compile + test runtime")
    println("  provided           - compile, tapi tidak di runtime (mis. Servlet API)")
    println("  runtime            - runtime saja, tidak untuk compile (mis. JDBC driver)")
    println()
    println("Contoh deklarasi di pom.xml:")
    println("  <dependency>")
    println("    <groupId>org.jetbrains.kotlinx</groupId>")
    println("    <artifactId>kotlinx-coroutines-core</artifactId>")
    println("    <version>1.8.1</version>")
    println("    <scope>compile</scope>")
    println("  </dependency>")
    println()
    println("Perintah pemeriksaan:")
    println("  mvn dependency:tree                  -> pohon lengkap")
    println("  mvn dependency:tree -Dscope=runtime  -> filter runtime saja")
    println("  mvn dependency:analyze               -> deteksi unused/undeclared")
    println()
    println("Repository default: Maven Central (https://repo1.maven.org/maven2/)")
    println("Mirror lokal: settings.xml di ~/.m2/ dapat mengarahkan ke Nexus/Artifactory")
}

Output yang Dihasilkan:

text
=== Dependensi Maven ===

Empat scope utama:
  compile (default) - tersedia di compile + runtime + transitif
  test               - hanya untuk test compile + test runtime
  provided           - compile, tapi tidak di runtime (mis. Servlet API)
  runtime            - runtime saja, tidak untuk compile (mis. JDBC driver)

Contoh deklarasi di pom.xml:
  <dependency>
    <groupId>org.jetbrains.kotlinx</groupId>
    <artifactId>kotlinx-coroutines-core</artifactId>
    <version>1.8.1</version>
    <scope>compile</scope>
  </dependency>

Perintah pemeriksaan:
  mvn dependency:tree                  -> pohon lengkap
  mvn dependency:tree -Dscope=runtime  -> filter runtime saja
  mvn dependency:analyze               -> deteksi unused/undeclared

Repository default: Maven Central (https://repo1.maven.org/maven2/)
Mirror lokal: settings.xml di ~/.m2/ dapat mengarahkan ke Nexus/Artifactory

Mari kita telaah kode di atas:

  1. Empat scope: ditampilkan dalam format cheat sheet. Pembaca dapat membandingkan dengan tabel di atas untuk memilih scope yang tepat saat menulis pom.xml baru.
  2. Contoh deklarasi: format XML lengkap. Maven sangat verbose dibanding Gradle, tetapi struktur ini konsisten untuk semua dependensi.
  3. mvn dependency:tree: perintah dependency:tree adalah bagian dari maven-dependency-plugin yang sudah terpasang default. Output menampilkan pohon dependensi lengkap dengan indentasi.
  4. mvn dependency:analyze: perintah ini mendeteksi dependensi yang dideklarasikan tetapi tidak dipakai (unused declared) serta dependensi yang dipakai tetapi tidak dideklarasikan secara eksplisit (used undeclared). Laporan ini sangat berguna untuk membersihkan pom.xml.

Repository Maven Central #

Repository default Maven adalah Maven Central (https://repo1.maven.org/maven2/). Maven mengunduh dependensi ke cache lokal di ~/.m2/repository/. Struktur direktori mengikuti groupId dengan titik diubah ke /. Mis. org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1 disimpan di:

text
~/.m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.8.1/
├── kotlinx-coroutines-core-1.8.1.jar
├── kotlinx-coroutines-core-1.8.1.pom
└── kotlinx-coroutines-core-1.8.1.jar.sha1

Repository Custom #

Untuk menambah repository custom (mis. Nexus internal atau JitPack), edit pom.xml:

xml pom.xml — custom repository
<repositories>
    <repository>
        <id>jitpack</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

Untuk konfigurasi global (mirror), edit ~/.m2/settings.xml:

xml ~/.m2/settings.xml — mirror
<settings>
    <mirrors>
        <mirror>
            <id>nexus-internal</id>
            <url>https://nexus.example.com/repository/maven-public/</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>
</settings>

dependency:analyze #

Perintah mvn dependency:analyze menghasilkan dua laporan:

  • Used undeclared: dependensi yang dipakai di kode tetapi tidak dideklarasikan secara eksplisit di pom.xml (hanya transitif). Risiko: bila versi dependensi transitif berubah, kode bisa pecah.
  • Unused declared: dependensi yang dideklarasikan tetapi tidak dipakai. Bisa dihapus untuk membersihkan POM.
text
$ mvn dependency:analyze
[INFO] Used undeclared dependencies:
    org.jetbrains.kotlin:kotlin-stdlib:2.0.21
[INFO] Unused declared dependencies:
    com.google.guava:guava:33.0.0-jre

Referensi Lanjutan #

  • Kotlin with Maven — panduan resmi Kotlin + Maven untuk deklarasi dependensi Kotlin stdlib dan library lain
  • Introduction to the POM — panduan resmi Maven untuk deklarasi dependensi, scope, dan transitive dependency

Penutup #

Pemahaman scope Maven dan mekanisme repository adalah kunci untuk menulis pom.xml yang benar. Untuk proyek Kotlin modern, Gradle adalah default, tetapi Maven tetap relevan untuk integrasi dengan organisasi yang sudah memegang ekosistem Maven. Pada chapter berikutnya kita akan membahas build variants — fitur Gradle untuk menghasilkan beberapa build dari satu proyek (debug/release, flavors).