Chapter sebelumnya menyebut Maven sebagai mesin yang mengompilasi, menguji, dan mengemas — tetapi Maven sejatinya tidak melakukan satu pun pekerjaan itu sendiri. Seluruh pekerjaan nyata dikerjakan oleh plugin: kumpulan goal yang diikat ke fase-fase lifecycle. Maven hanya menyediakan panggung dan urutan mainnya; plugin menyediakan pemainnya. Ketika kita mengetik mvn package, puluhan goal dari plugin inti berjalan tanpa kita sadari. Pada chapter ini kita akan memetakan goal-goal inti tersebut, menulis konfigurasi plugin di POM, dan mengikat goal tambahan ke fase pilihan kita.

Plugin dan Goal #

Setiap plugin memiliki koordinat GAV seperti artifact biasa — org.apache.maven.plugins:maven-compiler-plugin — dan di dalamnya tersimpan sejumlah goal. Nama goal ditulis pluginPrefix:namaGoal, misalnya compiler:compile. Goal dapat terikat bawaan ke sebuah fase (goal berjalan otomatis ketika fasenya tiba) atau dipanggil langsung dari baris perintah tanpa fase. Daftar plugin yang aktif beserta ikatan fasenya bisa kita lihat pada effective POM — sebagian besar ikatan bawaan sudah disediakan super POM untuk packaging jar.

Goal Bawaan yang Perlu Anda Kenal #

Program berikut merangkum sembilan goal inti yang menopang hampir setiap proyek Java, lengkap dengan fase bawaan tempat masing-masing goal terikat:

java PetaPlugin.java
public class PetaPlugin {

    // Goal plugin inti, fase bawaan tempat goal terikat, dan tugasnya.
    private static final String[][] GOAL = {
        {"resources:resources",      "process-resources",      "menyalin resource utama"},
        {"compiler:compile",         "compile",                "mengompilasi src/main/java"},
        {"resources:testResources",  "process-test-resources", "menyalin resource uji"},
        {"compiler:testCompile",     "test-compile",           "mengompilasi src/test/java"},
        {"surefire:test",            "test",                   "menjalankan pengujian unit"},
        {"jar:jar",                  "package",                "mengemas JAR aplikasi"},
        {"shade:shade",              "package",                "mengemas fat jar"},
        {"install:install",          "install",                "memasang ke ~/.m2"},
        {"deploy:deploy",            "deploy",                 "mengunggah ke repositori remote"}
    };

    public static void main(String[] args) {
        System.out.printf("%-25s %-23s %s%n", "GOAL", "FASE BAWAAN", "TUGAS");
        for (String[] baris : GOAL) {
            System.out.printf("%-25s %-23s %s%n", baris[0], baris[1], baris[2]);
        }
        System.out.println();
        System.out.println("total goal inti: " + GOAL.length);
    }
}

Output yang Dihasilkan:

text
GOAL                      FASE BAWAAN             TUGAS
resources:resources       process-resources       menyalin resource utama
compiler:compile          compile                 mengompilasi src/main/java
resources:testResources   process-test-resources  menyalin resource uji
compiler:testCompile      test-compile            mengompilasi src/test/java
surefire:test             test                    menjalankan pengujian unit
jar:jar                   package                 mengemas JAR aplikasi
shade:shade               package                 mengemas fat jar
install:install           install                 memasang ke ~/.m2
deploy:deploy             deploy                  mengunggah ke repositori remote

total goal inti: 9

Dari tabel tersebut terbaca tiga pola penting. Pertama, goal compile dari plugin compiler terikat di fase compile — penamaan yang sama antara fase dan goal memang disengaja agar mudah diingat, meskipun keduanya entitas berbeda. Kedua, dua goal berbeda (jar:jar dan shade:shade) terikat di fase yang sama (package); bila keduanya aktif, Maven menjalankannya berurutan. Ketiga, baris install:install dan deploy:deploy menegaskan bahwa memasang artifact ke repositori juga pekerjaan plugin, bukan fitur bawaan Maven.

Menyimpan Konfigurasi Plugin #

Saat perilaku bawaan kurang pas, konfigurasi plugin ditulis pada blok <build><plugins> di POM. Contoh berikut menyetel compiler agar menargetkan Java 21 dan menyalakan seluruh kategori peringatan -Xlint:

xml Konfigurasi maven-compiler-plugin
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.13.0</version>
            <configuration>
                <release>21</release>
                <compilerArgs>
                    <arg>-Xlint:all</arg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

Elemen <configuration> berisi parameter yang diteruskan ke goal — di sini <release>21</release> menjadi padanan flag --release yang kita pakai di chapter kompilasi manual, dan <arg>-Xlint:all</arg> mengaktifkan peringatan compiler yang sama. Konfigurasi pada level plugin berlaku untuk semua goal yang dijalankannya pada lifecycle.

Mengikat Eksekusi ke Fase #

Goal tanpa ikatan bawaan — seperti javadoc:jar milik plugin javadoc — tidak akan pernah berjalan lewat lifecycle sampai kita mengikatnya sendiri lewat blok <executions>:

xml Mengikat javadoc:jar ke fase package
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <version>3.6.3</version>
    <executions>
        <execution>
            <id>javadoc-jar</id>
            <phase>package</phase>
            <goals>
                <goal>jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Struktur <execution> memuat tiga bagian: <id> sebagai penanda unik eksekusi, <phase> sebagai tujuan pengikatan, dan <goals> sebagai daftar goal plugin yang dijalankan. Setelah pengikatan ini, setiap mvn package menghasilkan dua berkas sekaligus di target/: JAR aplikasi dari goal jar:jar dan JAR dokumentasi dari javadoc:jar.

Plugin Pengemasan Lanjutan #

Dua nama besar menutup daftar plugin pengemasan. maven-shade-plugin membangun fat jar seperti yang kita buat manual di chapter Packaging JAR — seluruh dependency digabung ke satu arsip, lengkap dengan opsi shading yang memindahkan class ke package baru agar tidak bertabrakan. maven-assembly-plugin lebih fleksibel untuk arsip bebas: deskriptor assembly mendefinisikan berkas apa saja yang ikut — biner, skrip, dokumentasi — sehingga cocok membangun distribusi zip/tar.gz aplikasi, bukan sekadar JAR. Keduanya lazim dipanggil lewat goal langsung (mvn assembly:single) maupun diikat ke fase package lewat <executions>.

Dengan lifecycle dan plugin, Maven menuntaskan otomasi build dari satu berkas XML. Di dunia JVM ada satu pesaing besar dengan pendekatan yang berbeda — kode Kotlin, task graph, dan build inkremental. Chapter berikutnya memperkenalkan Gradle.

Referensi #

Berkas Sumber

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

PetaPlugin.java

Unduh berkas
java
public class PetaPlugin {

    // Goal plugin inti, fase bawaan tempat goal terikat, dan tugasnya.
    private static final String[][] GOAL = {
        {"resources:resources",      "process-resources",      "menyalin resource utama"},
        {"compiler:compile",         "compile",                "mengompilasi src/main/java"},
        {"resources:testResources",  "process-test-resources", "menyalin resource uji"},
        {"compiler:testCompile",     "test-compile",           "mengompilasi src/test/java"},
        {"surefire:test",            "test",                   "menjalankan pengujian unit"},
        {"jar:jar",                  "package",                "mengemas JAR aplikasi"},
        {"shade:shade",              "package",                "mengemas fat jar"},
        {"install:install",          "install",                "memasang ke ~/.m2"},
        {"deploy:deploy",            "deploy",                 "mengunggah ke repositori remote"}
    };

    public static void main(String[] args) {
        System.out.printf("%-25s %-23s %s%n", "GOAL", "FASE BAWAAN", "TUGAS");
        for (String[] baris : GOAL) {
            System.out.printf("%-25s %-23s %s%n", baris[0], baris[1], baris[2]);
        }
        System.out.println();
        System.out.println("total goal inti: " + GOAL.length);
    }
}