Membangun proyek Spring Boot dari nol secara manual menuntut keputusan yang panjang: parent POM, starter mana, versi berapa, struktur direktori seperti apa, dan berkas uji apa yang wajib ada. Spring Initializr memangkas seluruh keputusan itu menjadi satu form: pilih build tool, versi Boot, koordinat proyek, dan daftar dependensi — lalu situs itu menghasilkan kerangka proyek lengkap yang bisa diunduh sebagai ZIP. Pada Chapter ini kita telusuri form Initializr, bedah struktur hasil unduhannya, dan pahami berkas-berkas penting di dalamnya.
Form start.spring.io #
Alamat resminya adalah start.spring.io . Formulirnya meminta keputusan-keputusan berikut:
| Bidang | Pilihan umum | Keterangan |
|---|---|---|
| Project | Maven / Gradle | build tool penjaga dependensi |
| Language | Java / Kotlin / Groovy | bahasa sumber |
| Spring Boot | 3.5.x | versi stabil Boot terpilih |
| Packaging | Jar | fat jar untuk server tertanam |
| Java | 21 | baseline LTS yang didukung |
| Dependencies | starter pilihan | ditambahkan lewat pencarian |
Bagian Dependencies adalah inti pengalaman Initializr. Kolom pencarian menerima nama teknologi (“web”, “jpa”, “security”) lalu menawarkan starter yang cocok. Pilihan kita di bidang-bidang lain menyesuaikan tawaran: misalnya versi Boot yang lebih tua membatasi pilihan versi Java. Tim pengembang internal perusahaan juga bisa meng-host Initializr sendiri dengan katalog starter khusus, karena mesinnya tersedia sebagai pustaka dan layanan.
Struktur Proyek Hasil Unduhan #
ZIP hasil unduhan memuat proyek Maven siap ekspor. Berikut struktur tipikalnya untuk proyek layanan dengan starter web — ditampilkan apa adanya:
layanan/
|-- .gitattributes
|-- .gitignore
|-- mvnw
|-- mvnw.cmd
|-- pom.xml
`-- src/
|-- main/
| |-- java/
| | `-- id/example/layanan/
| | `-- LayananApplication.java
| `-- resources/
| `-- application.properties
`-- test/
`-- java/
`-- id/example/layanan/
`-- LayananApplicationTests.javaBerkas mvnw adalah Maven wrapper: skrip yang mengunduh versi Maven yang tepat sehingga setiap anggota tim dan pipeline CI memakai Maven yang sama persis tanpa instalasi manual. Berkas .gitignore bawaannya sudah menyaring hasil build target/.
Untuk melihat pentingnya setiap berkas, kita bangun ulang struktur itu secara programatik dan periksa kelengkapannya:
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class KerangkaProyek {
// Struktur direktori sederhana untuk pohon berkas.
static class Node {
final TreeMap<String, Node> anak = new TreeMap<>();
boolean berkas = false;
}
private static final List<String> BERKAS = List.of(
".gitattributes",
".gitignore",
"mvnw",
"mvnw.cmd",
"pom.xml",
"src/main/java/id/example/layanan/LayananApplication.java",
"src/main/resources/application.properties",
"src/main/resources/static/index.html",
"src/main/resources/templates/beranda.html",
"src/test/java/id/example/layanan/LayananApplicationTests.java");
private static final List<String> WAJIB = List.of(
"pom.xml",
"mvnw",
"src/main/java/id/example/layanan/LayananApplication.java",
"src/main/resources/application.properties",
"src/test/java/id/example/layanan/LayananApplicationTests.java");
public static void main(String[] args) {
Node akar = new Node();
for (String berkas : BERKAS) {
Node sekarang = akar;
String[] bagian = berkas.split("/");
for (int i = 0; i < bagian.length; i++) {
sekarang = sekarang.anak.computeIfAbsent(bagian[i], k -> new Node());
if (i == bagian.length - 1) {
sekarang.berkas = true;
}
}
}
System.out.println("layanan/");
cetakPohon(akar, "");
System.out.println();
System.out.println("== pemeriksaan struktur hasil Initializr ==");
int kurang = 0;
for (String wajib : WAJIB) {
boolean ada = BERKAS.contains(wajib);
System.out.println((ada ? "[OK] " : "[KURANG] ") + wajib);
if (!ada) {
kurang++;
}
}
System.out.println();
System.out.println(kurang == 0
? "Proyek lengkap: siap diimpor ke IDE dan dijalankan."
: "Terdapat " + kurang + " berkas wajib yang hilang.");
}
private static void cetakPohon(Node node, String prefiks) {
Map<String, Node> anak = node.anak;
int i = 0;
for (Map.Entry<String, Node> e : anak.entrySet()) {
String cabang = (i == anak.size() - 1) ? "`-- " : "|-- ";
String tipe = e.getValue().berkas ? "" : "/";
System.out.println(prefiks + cabang + e.getKey() + tipe);
cetakPohon(e.getValue(), prefiks + (i == anak.size() - 1 ? " " : "| "));
i++;
}
}
}Output yang Dihasilkan:
layanan/
|-- .gitattributes
|-- .gitignore
|-- mvnw
|-- mvnw.cmd
|-- pom.xml
`-- src/
|-- main/
| |-- java/
| | `-- id/
| | `-- example/
| | `-- layanan/
| | `-- LayananApplication.java
| `-- resources/
| |-- application.properties
| |-- static/
| | `-- index.html
| `-- templates/
| `-- beranda.html
`-- test/
`-- java/
`-- id/
`-- example/
`-- layanan/
`-- LayananApplicationTests.java
== pemeriksaan struktur hasil Initializr ==
[OK] pom.xml
[OK] mvnw
[OK] src/main/java/id/example/layanan/LayananApplication.java
[OK] src/main/resources/application.properties
[OK] src/test/java/id/example/layanan/LayananApplicationTests.java
Proyek lengkap: siap diimpor ke IDE dan dijalankan.Mari kita bedah program di atas:
class NodedenganTreeMap<String, Node> anak: struktur pohon direktori.TreeMapmenjaga anak tersortir menurut abjad sehingga pohon yang tercetak selalu sama urutannya —application.propertiesselalu di atasstatic/.- Loop
BERKAS: setiap path dipecah dengansplit("/")lalu node dibuat bertingkat dengancomputeIfAbsent; node paling bawah ditandaiberkas = trueuntuk membedakan berkas dari direktori. cetakPohon(node, prefiks): method rekursif merender cabang dengan garis|--dan backtick`--untuk anak terakhir, memakai prefiks spasi yang menjorok sesuai kedalaman — teknik yang sama dipakai perintahtree.- Pemeriksaan
WAJIB: daftar berkas wajib Initializr diperiksa satu per satu terhadap struktur; hasil[OK]untuk semua menandakan proyek lengkap. Dalam CI, pemeriksaan serupa sering dipakai sebagai sanity check artefak.
Tiga Berkas Kunci #
pom.xml adalah pusat kendali dependensi — ditampilkan apa adanya untuk Spring Boot 3.5:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/>
</parent>
<groupId>id.example</groupId>
<artifactId>layanan</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>layanan</name>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>LayananApplication.java adalah titik masuk aplikasi — ditampilkan apa adanya:
@SpringBootApplication
public class LayananApplication {
public static void main(String[] args) {
SpringApplication.run(LayananApplication.class, args);
}
}Satu anotasi @SpringBootApplication sekaligus memuat tiga anotasi: @SpringBootConfiguration menandai kelas konfigurasi, @EnableAutoConfiguration mengaktifkan auto-configuration, dan @ComponentScan memindai paket kelas ini ke bawah untuk menemukan komponen. Karena itu kelas aplikasi dianjurkan berada di paket akar — seluruh kode lain hidup di bawahnya agar ikut terpindai.
LayananApplicationTests.java sudah berisi satu uji konteks: @SpringBootTest memastikan context aplikasi bisa naik — jaring pengaman pertama bahwa seluruh bean tersambung. Setelah kerangka berdiri, langkah berikutnya adalah mengisinya dengan konfigurasi aplikasi lewat application.properties, bahasan Chapter berikutnya.
Referensi #
- Spring Initializr
- Spring Boot Reference Documentation
- Spring Guides: Building an Application with Spring Boot
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
KerangkaProyek.java
Unduh berkasimport java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class KerangkaProyek {
// Struktur direktori sederhana untuk pohon berkas.
static class Node {
final TreeMap<String, Node> anak = new TreeMap<>();
boolean berkas = false;
}
private static final List<String> BERKAS = List.of(
".gitattributes",
".gitignore",
"mvnw",
"mvnw.cmd",
"pom.xml",
"src/main/java/id/example/layanan/LayananApplication.java",
"src/main/resources/application.properties",
"src/main/resources/static/index.html",
"src/main/resources/templates/beranda.html",
"src/test/java/id/example/layanan/LayananApplicationTests.java");
private static final List<String> WAJIB = List.of(
"pom.xml",
"mvnw",
"src/main/java/id/example/layanan/LayananApplication.java",
"src/main/resources/application.properties",
"src/test/java/id/example/layanan/LayananApplicationTests.java");
public static void main(String[] args) {
Node akar = new Node();
for (String berkas : BERKAS) {
Node sekarang = akar;
String[] bagian = berkas.split("/");
for (int i = 0; i < bagian.length; i++) {
sekarang = sekarang.anak.computeIfAbsent(bagian[i], k -> new Node());
if (i == bagian.length - 1) {
sekarang.berkas = true;
}
}
}
System.out.println("layanan/");
cetakPohon(akar, "");
System.out.println();
System.out.println("== pemeriksaan struktur hasil Initializr ==");
int kurang = 0;
for (String wajib : WAJIB) {
boolean ada = BERKAS.contains(wajib);
System.out.println((ada ? "[OK] " : "[KURANG] ") + wajib);
if (!ada) {
kurang++;
}
}
System.out.println();
System.out.println(kurang == 0
? "Proyek lengkap: siap diimpor ke IDE dan dijalankan."
: "Terdapat " + kurang + " berkas wajib yang hilang.");
}
private static void cetakPohon(Node node, String prefiks) {
Map<String, Node> anak = node.anak;
int i = 0;
for (Map.Entry<String, Node> e : anak.entrySet()) {
String cabang = (i == anak.size() - 1) ? "`-- " : "|-- ";
String tipe = e.getValue().berkas ? "" : "/";
System.out.println(prefiks + cabang + e.getKey() + tipe);
cetakPohon(e.getValue(), prefiks + (i == anak.size() - 1 ? " " : "| "));
i++;
}
}
}