Kompilasi manual dan pengemasan JAR di dua chapter sebelumnya menunjukkan pola kerja yang berulang: bersihkan output, kompilasi, uji, kemas, ulangi. Maven — build tool dari Apache yang lahir tahun 2004 — mengotomatiskan seluruh rangkaian itu dari satu berkas deskripsi proyek bernama pom.xml. Filosofi kerjanya, convention over configuration, membuat Maven mengasumsikan struktur direktori dan urutan langkah build yang standar sehingga kita cukup mendeskripsikan pengecualian. Pada chapter ini kita akan membedah anatomi POM, koordinat GAV yang menjadi identitas setiap artifact, serta bagaimana Maven menemukan dependency lewat repositori lokal dan Maven Central.
Apa itu Maven #
Maven adalah tiga hal sekaligus. Pertama, build tool: Maven menjalankan kompilasi, pengujian, dan packaging melalui rangkaian fase yang tetap urutannya. Kedua, dependency manager: Maven mengunduh library eksternal beserta dependency-nya yang transitif, lalu menyusun classpath secara otomatis. Ketiga, project model: POM mendeskripsikan proyek — nama, versi, packaging, dependensi — dalam format XML yang dapat dibaca alat lain, termasuk IDE.
Karena seluruh konfigurasi hidup di pom.xml, anggota tim baru tidak perlu menyalin skrip build dari rekan kerjanya; cukup menjalankan perintah yang sama pada berkas yang sama dan Maven akan membangun proyek dengan perilaku identik.
Convention over Configuration #
Build tool generatif lain di masa lalu menuntut kita menulis skrip langkah demi langkah. Maven membalik pendekatannya: Maven sudah menetapkan di mana kode sumber berada (src/main/java), di mana kode uji berada (src/test/java), di mana output ditulis (target/), dan urutan fase build dari validate sampai deploy. Selama proyek mengikuti konvensi tersebut, tidak ada satu baris konfigurasi pun yang perlu ditulis untuk hal-hal tersebut.
Konfigurasi baru diperlukan saat menyimpang dari konvensi — misalnya mengubah versi Java target atau menambah dependency. Hasilnya, POM proyek biasa hanya berisi belasan baris yang semuanya bermakna, bukan ratusan baris instruksi mekanis.
POM sebagai Pusat Kendali #
Berkas pom.xml berikut adalah deskripsi minimum sebuah aplikasi Java bernama aplikasi-gudang:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.toko.gudang</groupId>
<artifactId>aplikasi-gudang</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>Mari kita bedah elemen-elemennya:
<modelVersion>: versi model POM yang dipakai; nilainya4.0.0untuk seluruh Maven modern.<groupId>: identitas organisasi pembuat, lazim berupa domain terbalik seperticom.toko.gudang.<artifactId>: nama proyek atau modul; hasil build biasanya bernamaartifactId-version.jar.<version>: versi artifact saat ini, mengikuti polaMAJOR.MINOR.PATCH.<packaging>: bentuk hasil akhir —jaruntuk library/aplikasi,waruntuk aplikasi web,pomuntuk agregator multi-modul.<properties>: variabel konfigurasi;maven.compiler.releasemenyetel opsi--releasecompiler, dansourceEncodingmemastikan berkas sumber dibaca sebagai UTF-8.
Dengan POM sekecil itu pun, Maven sudah tahu harus berbuat apa. Perintah mvn clean package membersihkan direktori target/, mengompilasi seluruh src/main/java, menjalankan pengujian dari src/test/java, lalu menghasilkan target/aplikasi-gudang-1.0.0.jar — semuanya dari konvensi, tanpa satu instruksi build pun yang kita tulis.
Koordinat GAV #
Setiap artifact di dunia Maven diidentifikasi oleh tiga bagian yang disebut koordinat GAV: groupId, artifactId, dan version. Format penulisannya adalah groupId:artifactId:version — misalnya org.apache.commons:commons-lang3:3.14.0. Program kecil berikut memecah koordinat GAV dan menurunkan lokasi penyimpanannya di repositori:
public class KoordinatGav {
public static void main(String[] args) {
String[] daftarGav = {
"org.apache.commons:commons-lang3:3.14.0",
"com.toko.gudang:aplikasi-gudang:1.0.0",
"junit:junit:4.13.2"
};
for (String gav : daftarGav) {
String[] bagian = gav.split(":");
System.out.printf("GAV : %s%n", gav);
System.out.printf("groupId : %s%n", bagian[0]);
System.out.printf("artifactId : %s%n", bagian[1]);
System.out.printf("version : %s%n", bagian[2]);
System.out.printf("path repo : %s%n", kePathRepositori(bagian));
System.out.println();
}
}
/** Maven menyimpan artifact dengan pola groupId(dots->slash)/artifactId/version/ */
private static String kePathRepositori(String[] bagian) {
String grup = bagian[0].replace('.', '/');
return grup + "/" + bagian[1] + "/" + bagian[2]
+ "/" + bagian[1] + "-" + bagian[2] + ".jar";
}
}Output yang Dihasilkan:
GAV : org.apache.commons:commons-lang3:3.14.0
groupId : org.apache.commons
artifactId : commons-lang3
version : 3.14.0
path repo : org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar
GAV : com.toko.gudang:aplikasi-gudang:1.0.0
groupId : com.toko.gudang
artifactId : aplikasi-gudang
version : 1.0.0
path repo : com/toko/gudang/aplikasi-gudang/1.0.0/aplikasi-gudang-1.0.0.jar
GAV : junit:junit:4.13.2
groupId : junit
artifactId : junit
version : 4.13.2
path repo : junit/junit/4.13.2/junit-4.13.2.jarTiga hal terlihat dari output di atas:
- Baris
GAVsampaiversion: satu koordinat terurai menjadi tiga field — inilah cara Maven (dan pengguna Maven Central) menyebut artifact tanpa ambigu. - Baris
path repo: setiap titik padagroupIdmenjadi pemisah direktori, diikutiartifactId/version, sehinggaorg.apache.commons:commons-lang3:3.14.0tersimpan diorg/apache/commons/commons-lang3/3.14.0/. - Nama berkas: polanya
artifactId-version.jar, misalnyaaplikasi-gudang-1.0.0.jar— alasan Maven dapat menebak nama berkas hasil build dari POM.
Super POM dan POM Efektif #
POM Anda tidak pernah berdiri sendiri. Setiap POM mewarisi super POM — definisi bawaan Maven yang memuat repositori Central, struktur direktori standar, dan goal plugin inti. Gabungan super POM, POM proyek, dan (bila ada) POM induk disebut effective POM. Maven selalu membaca effective POM inilah, bukan berkas pom.xml mentah Anda.
Konsekuensinya praktis: mvn help:effective-pom menampilkan POM final yang benar-benar dipakai — alat diagnosis utama ketika perilaku build tidak sesuai dugaan. Pada proyek multi-modul, POM induk (packaging bernilai pom) menyimpan konfigurasi bersama seperti versi Java dan daftar dependency, lalu setiap modul mewarisinya dan hanya menambahkan bagian khas modul.
Repositori Lokal dan Maven Central #
Saat Maven menemukan deklarasi dependency, Maven mencari berkas JAR dalam urutan tetap: repositori lokal di ~/.m2/repository lebih dulu, lalu repositori remote seperti Maven Central (repo.maven.apache.org). Berkas yang ditemukan di remote diunduh sekali dan disimpan ke repositori lokal — build berikutnya membaca dari disk sehingga cepat dan tetap berjalan saat jaringan tidak tersedia. Peta prosesnya:
Path penyimpanan lokal mengikuti pola pada program KoordinatGav di atas, sehingga kita bisa menelusuri isi ~/.m2/repository secara manual untuk memastikan versi mana yang sudah ada di mesin.
Filosofi convention over configuration, koordinat GAV, dan repositori adalah tiga fondasi Maven. Chapter berikutnya menelusuri struktur direktori standar yang diisyaratkan konvensi tersebut — dari src/main/java hingga target/ — beserta pemetaan groupId ke struktur package.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
KoordinatGav.java
Unduh berkaspublic class KoordinatGav {
public static void main(String[] args) {
String[] daftarGav = {
"org.apache.commons:commons-lang3:3.14.0",
"com.toko.gudang:aplikasi-gudang:1.0.0",
"junit:junit:4.13.2"
};
for (String gav : daftarGav) {
String[] bagian = gav.split(":");
System.out.printf("GAV : %s%n", gav);
System.out.printf("groupId : %s%n", bagian[0]);
System.out.printf("artifactId : %s%n", bagian[1]);
System.out.printf("version : %s%n", bagian[2]);
System.out.printf("path repo : %s%n", kePathRepositori(bagian));
System.out.println();
}
}
/** Maven menyimpan artifact dengan pola groupId(dots->slash)/artifactId/version/ */
private static String kePathRepositori(String[] bagian) {
String grup = bagian[0].replace('.', '/');
return grup + "/" + bagian[1] + "/" + bagian[2]
+ "/" + bagian[1] + "-" + bagian[2] + ".jar";
}
}