Bài này dựng dự án và giải thích từng mảnh của nó.

start.spring.io

Cách chuẩn để bắt đầu. Chọn Maven hoặc Gradle, Java 21, Spring Boot 3.x, rồi thêm dependency.

Hoặc bằng dòng lệnh:

curl https://start.spring.io/starter.zip \
  -d dependencies=web,actuator \
  -d javaVersion=21 -d bootVersion=3.3.4 \
  -d groupId=vd -d artifactId=demo -o demo.zip

Đừng chép pom.xml từ dự án cũ — phiên bản Boot kéo theo hàng trăm phiên bản thư viện đã được kiểm thử cùng nhau, và start.spring.io luôn cho bộ mới nhất tương thích.

pom.xml: ba phần đáng đọc

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.3.4</version>
</parent>

Parent POM khai dependencyManagement cho hàng trăm thư viện. Đây là lý do bạn viết dependency không có số phiên bản — và là lý do dự án Spring Boot hiếm khi gặp xung đột phiên bản. Bài 91 sê-ri Java đã đo: chỉ cần đảo thứ tự hai dòng khai báo là phiên bản thư viện đổi; BOM của Boot chặn chuyện đó.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Starter là một POM rỗng chỉ để kéo theo một nhóm thư viện. starter-web mang Spring MVC, Tomcat nhúng, Jackson, validation. Bạn khai một dòng thay vì mười lăm.

<plugin>
  <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

Plugin đóng gói thành fat jar và cho mvn spring-boot:run.

Fat jar

  fat jar: 22 MB
mvn package
java -jar target/demo-1.0.jar

Không cần cài Tomcat, không triển khai WAR. Máy chủ web nằm trong jar.

Cấu trúc bên trong không phải jar thường:

  BOOT-INF/classes/     mã của bạn
  BOOT-INF/lib/         mọi jar phụ thuộc, NGUYÊN VẸN
  org/springframework/boot/loader/   trình nạp riêng

Java không nạp được jar lồng trong jar, nên Boot có trình nạp lớp riêng. Đó cũng là lý do fat jar của Boot khác "shaded jar" — thư viện không bị trộn vào nhau nên không có xung đột tệp tài nguyên.

Với Docker, cấu trúc này còn cho phép tách tầng để tận dụng cache — bài 55 sẽ nói.

Lớp khởi động

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

@SpringBootApplication gộp ba chú thích:

@SpringBootConfiguration — đánh dấu đây là nguồn định nghĩa bean. @EnableAutoConfiguration — bật cấu hình tự động, bài 8. @ComponentScan — quét package của lớp này và mọi package con để tìm @Component.

Điểm cuối rất quan trọng: đặt lớp App ở package gốc. Đặt nó trong vd.demo.web thì Spring không thấy vd.demo.kho, và bạn nhận lỗi "no qualifying bean" rất khó hiểu.

Cấu trúc thư mục

demo/
├── pom.xml
└── src/
    ├── main/java/vd/demo/     App.java + mã
    ├── main/resources/         application.yml, static/, templates/
    └── test/java/vd/demo/

Trong src/main/java, tổ chức theo miền nghiệp vụ:

  vd/demo/
  ├── App.java
  ├── donhang/     Controller, Service, Repository, entity của đơn hàng
  └── khachhang/

thay vì theo tầng kỹ thuật (controllers/, services/, repositories/). Cùng lý do với bài 21 sê-ri Go: tách theo tầng nghĩa là mỗi thay đổi phải sửa nhiều package.

Chạy

mvn spring-boot:run          # phát triển, có restart nhanh
java -jar target/demo.jar    # sản xuất

Thêm spring-boot-devtools để tự khởi động lại khi mã đổi — chỉ dùng lúc phát triển, và nó tự tắt khi chạy từ jar.

application.yml

server:
  port: 8081
spring:
  application:
    name: dich-vu-don-hang
logging:
  level:
    root: INFO

YAML hay properties đều được. Bài 6 sẽ nói về thứ tự ưu tiên và profile.

Ba lệnh đầu tiên nên biết

mvn dependency:tree | head -40      # xem starter kéo theo gì
curl localhost:8080/actuator/health # kiểm tra sống
mvn spring-boot:run -Ddebug         # xem auto-config quyết định gì

Lệnh thứ ba đáng chạy một lần ngay hôm nay — nó in ra toàn bộ quyết định của Boot, và bài 8 sẽ đọc kết quả đó.

Thử ba mươi giây

mvn dependency:tree | grep -c ':.*:.*:'

Đếm số thư viện mà một dòng spring-boot-starter-web kéo vào. Con số thường trên bốn mươi — và đó là lời giải thích trực quan nhất cho 266 bean ở bài trước.

Ngày mai: IoC container và bean — Spring quản lý những đối tượng đó thế nào.