Perintah mvn clean package yang singkat itu ternyata menjalankan belasan langkah berurutan: memeriksa proyek, mengompilasi, menguji, lalu mengemas. Mesin di balik urutan tersebut bernama lifecycle — daftar fase tetap yang dikenal Maven tanpa perlu kita definisikan ulang. Kita tidak memerintahkan Maven “kompilasi lalu uji lalu kemas”; kita cukup menyebut satu fase tujuan, dan Maven menjalankan fase tersebut beserta seluruh fase pendahulunya. Pada chapter ini kita akan menelusuri urutan fase lifecycle bawaan, membedakan konsep fase dari konsep goal, dan memahami arti perintah favorit semua orang: mvn clean install.

Tiga Lifecycle Bawaan #

Maven mengenal tiga lifecycle yang saling lepas. Lifecycle clean (pre-clean, clean, post-clean) fokus menghapus target/. Lifecycle default adalah jalan raya dari validate sampai deploy yang menampung kompilasi hingga publikasi artifact. Lifecycle site membangun dokumentasi situs proyek. Ketiganya tidak berpindah otomatis satu ke yang lain — itulah alasan clean ditulis terpisah pada mvn clean package: perintah itu menjalankan lifecycle clean lebih dulu, dilanjutkan lifecycle default sampai fase package.

Menjalankan Fase #

Perintah Maven menerima nama fase sebagai argumen. Fase mana pun yang kita pilih, Maven selalu memulai dari validate dan berjalan berurutan sampai fase tujuan tercapai:

bash
mvn compile
mvn test
mvn clean package
mvn clean install

Empat perintah di atas memperlihatkan sifat berurutan tersebut. mvn compile berhenti setelah fase compile — pengujian belum berjalan. mvn test menambahkan kompilasi kode uji dan eksekusi pengujian. mvn clean package melangkah sampai pengemasan JAR di target/. mvn clean install berjalan paling jauh di antara keempatnya: setelah JAR selesai dibangun, fase install menyalin artifact ke repositori lokal ~/.m2/repository sehingga modul atau proyek lain di mesin yang sama dapat menggunakannya sebagai dependency dengan koordinat GAV proyek kita.

Daftar Fase Lifecycle Default #

Lifecycle default memuat 23 fase. Program berikut menyimpan urutannya dan mencetak seluruh fase yang berjalan hingga target yang diberikan — package secara bawaan, atau fase lain melalui argumen:

public class SimulasiFase {

    // Urutan fase lifecycle default Maven beserta ringkasan tugasnya.
    private static final String[] FASE = {
        "validate", "initialize", "generate-sources", "process-sources",
        "generate-resources", "process-resources", "compile", "process-classes",
        "generate-test-sources", "process-test-sources", "generate-test-resources",
        "process-test-resources", "test-compile", "process-test-classes", "test",
        "prepare-package", "package", "pre-integration-test", "integration-test",
        "post-integration-test", "verify", "install", "deploy"
    };

    private static final String[] TUGAS = {
        "memeriksa validitas proyek",
        "menyiapkan status build",
        "menghasilkan kode sumber tambahan",
        "memproses kode sumber",
        "menghasilkan resource",
        "menyalin resource ke target/classes",
        "mengompilasi src/main/java",
        "pascaproses bytecode utama",
        "menghasilkan kode uji tambahan",
        "memproses kode uji",
        "menghasilkan resource uji",
        "menyalin resource ke target/test-classes",
        "mengompilasi src/test/java",
        "pascaproses bytecode uji",
        "menjalankan pengujian unit",
        "menyiapkan kemasan final",
        "mengemas JAR/WAR di target/",
        "menyiapkan lingkungan integrasi",
        "menjalankan pengujian integrasi",
        "membersihkan lingkungan integrasi",
        "memeriksa hasil quality gate",
        "memasang artifact ke ~/.m2",
        "mengunggah artifact ke repositori remote"
    };

    public static void main(String[] args) {
        String target = args.length > 0 ? args[0] : "package";
        int batas = -1;
        for (int i = 0; i < FASE.length; i++) {
            if (FASE[i].equals(target)) {
                batas = i;
                break;
            }
        }
        if (batas < 0) {
            System.out.println("fase tidak dikenal: " + target);
            return;
        }

        System.out.println("$ mvn " + target);
        for (int i = 0; i <= batas; i++) {
            System.out.printf("%2d. %-24s - %s%n", i + 1, FASE[i], TUGAS[i]);
        }
        System.out.println();
        System.out.println("total fase yang dijalankan: " + (batas + 1));
    }
}

Output yang Dihasilkan:

text
$ mvn package
 1. validate                 - memeriksa validitas proyek
 2. initialize               - menyiapkan status build
 3. generate-sources         - menghasilkan kode sumber tambahan
 4. process-sources          - memproses kode sumber
 5. generate-resources       - menghasilkan resource
 6. process-resources        - menyalin resource ke target/classes
 7. compile                  - mengompilasi src/main/java
 8. process-classes          - pascaproses bytecode utama
 9. generate-test-sources    - menghasilkan kode uji tambahan
10. process-test-sources     - memproses kode uji
11. generate-test-resources  - menghasilkan resource uji
12. process-test-resources   - menyalin resource ke target/test-classes
13. test-compile             - mengompilasi src/test/java
14. process-test-classes     - pascaproses bytecode uji
15. test                     - menjalankan pengujian unit
16. prepare-package          - menyiapkan kemasan final
17. package                  - mengemas JAR/WAR di target/

total fase yang dijalankan: 17

Mari kita bedah logika program dan hubungannya dengan Maven:

  1. Array FASE: menyimpan 23 fase dalam urutan resmi lifecycle default — dari validate sampai deploy; urutan inilah yang Maven ikuti setiap kali kita memanggil sebuah fase.
  2. Array TUGAS: ringkasan satu baris per fase, sejajar indeks dengan FASE — cara sederhana memetakan dua data paralel.
  3. Baris pencarian batas: mencari posisi fase target; menyebut test berarti Maven berhenti setelah fase ke-15, menyebut package berhenti di fase ke-17.
  4. Perulangan cetak: hanya mencetak fase 0 sampai batas — visualisasi prinsip “menyebut satu fase berarti menjalankan semua pendahulunya”.
  5. Baris args.length > 0: program menerima target dari argumen; menjalankan program dengan argumen install mencetak 22 fase, hanya menyisakan deploy.

Fase yang paling sering Anda lewati tanpa sadar justru yang paling penting: compile di posisi 7 dan test di posisi 15 adalah dua momen ketika compiler dan framework pengujian benar-benar bekerja — selebihnya berupa persiapan dan pembersihan.

Fase versus Goal #

Fase hanyalah penanda posisi dalam urutan — fase tidak tahu cara mengompilasi atau menguji. Pekerjaan nyata dikerjakan oleh goal: unit kerja milik plugin, misalnya compiler:compile (goal compile dari plugin maven-compiler-plugin) atau surefire:test. Maven mengikat goal ke fase — itulah sebabnya fase compile “tahu” harus mengompilasi: goal compiler:compile terpasang padanya.

Konsekuensinya ada dua. Pertama, satu fase dapat menjalankan beberapa goal sekaligus dari plugin berbeda, berurutan sesuai deklarasi POM. Kedua, goal juga dapat dipanggil langsung tanpa fase:

bash
mvn compiler:compile
mvn dependency:tree

Pemanggilan langsung seperti dependency:tree mengeksekusi satu goal itu saja — berguna untuk diagnostik — tetapi melewati urutan fase, jadi tidak ada jaminan fase pendahulu sudah berjalan. Sebagai aturan praktis: fase untuk alur build normal, goal langsung untuk alat bantu diagnostik.

Lifecycle mengatur kapan pekerjaan berjalan, plugin menyediakan apa pekerjaannya. Chapter berikutnya membedah plugin-plugin inti Maven — dari compiler hingga shade — dan cara mengikat konfigurasinya ke fase.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

SimulasiFase.java

Unduh berkas
java
public class SimulasiFase {

    // Urutan fase lifecycle default Maven beserta ringkasan tugasnya.
    private static final String[] FASE = {
        "validate", "initialize", "generate-sources", "process-sources",
        "generate-resources", "process-resources", "compile", "process-classes",
        "generate-test-sources", "process-test-sources", "generate-test-resources",
        "process-test-resources", "test-compile", "process-test-classes", "test",
        "prepare-package", "package", "pre-integration-test", "integration-test",
        "post-integration-test", "verify", "install", "deploy"
    };

    private static final String[] TUGAS = {
        "memeriksa validitas proyek",
        "menyiapkan status build",
        "menghasilkan kode sumber tambahan",
        "memproses kode sumber",
        "menghasilkan resource",
        "menyalin resource ke target/classes",
        "mengompilasi src/main/java",
        "pascaproses bytecode utama",
        "menghasilkan kode uji tambahan",
        "memproses kode uji",
        "menghasilkan resource uji",
        "menyalin resource ke target/test-classes",
        "mengompilasi src/test/java",
        "pascaproses bytecode uji",
        "menjalankan pengujian unit",
        "menyiapkan kemasan final",
        "mengemas JAR/WAR di target/",
        "menyiapkan lingkungan integrasi",
        "menjalankan pengujian integrasi",
        "membersihkan lingkungan integrasi",
        "memeriksa hasil quality gate",
        "memasang artifact ke ~/.m2",
        "mengunggah artifact ke repositori remote"
    };

    public static void main(String[] args) {
        String target = args.length > 0 ? args[0] : "package";
        int batas = -1;
        for (int i = 0; i < FASE.length; i++) {
            if (FASE[i].equals(target)) {
                batas = i;
                break;
            }
        }
        if (batas < 0) {
            System.out.println("fase tidak dikenal: " + target);
            return;
        }

        System.out.println("$ mvn " + target);
        for (int i = 0; i <= batas; i++) {
            System.out.printf("%2d. %-24s - %s%n", i + 1, FASE[i], TUGAS[i]);
        }
        System.out.println();
        System.out.println("total fase yang dijalankan: " + (batas + 1));
    }
}