Tổng quan Spring Boot
Xem thửMột nhóm mới vào dự án cần cung cấp REST API đầu tiên. Với Spring Framework thuần, họ phải tự đăng ký DispatcherServlet, chọn bộ chuyển đổi JSON, cấu hình nguồn dữ liệu, rồi đóng gói WAR để chạy trên một Tomcat cài sẵn. Mất vài ngày mới có một endpoint trả về ok, và mỗi dịch vụ lại được cấu hình hơi khác nhau, nên khi người này nghỉ việc, không ai dám động vào cấu hình của dịch vụ của họ. Spring Boot giải quyết đúng điểm đó: bạn khai báo dependency và vài property, framework tự lắp ráp một ứng dụng chạy được, theo cùng một quy ước ở mọi dịch vụ.
Bài mở đầu này không dạy cú pháp. Nó dựng bản đồ để các bài sau có chỗ đặt: Spring Boot làm gì và không làm gì, một ứng dụng Boot thực chất là gì (một tiến trình JVM chứa cả máy chủ web), vì sao khi thiếu cấu hình bắt buộc nó dừng thay vì chạy tiếp, và cách khóa học này làm việc: mọi con số và thông báo lỗi trong bài đều đo từ các dự án thật (Spring Boot 4.1.1, Spring Framework 7.0.9, JDK 17, Maven 3.9.12), không sao chép từ tài liệu. Cuối bài có một bài thực hành tạo dự án đầu tiên và kiểm chứng nó.
Sau bài này, bạn có thể
- Giải thích Spring Boot giúp những việc gì và không thay thế điều gì.
- Mô tả đường đi của một request qua một ứng dụng Spring Boot điển hình.
- Giải thích vì sao ứng dụng dừng ngay khi thiếu một cấu hình bắt buộc, và đọc báo cáo lỗi đó.
- Chuẩn bị môi trường (JDK, Maven) và tạo, chạy, kiểm tra dự án đầu tiên.
Bài này dành cho người đã biết Java cơ bản và Maven ở mức khai báo dependency. Khóa học dùng Spring Boot 4.x (Spring Framework 7, Java 17 trở lên); chỗ nào khác với 3.x, bài sẽ ghi chú riêng.
Spring Boot giải quyết những việc gì
| Nhu cầu | Cách Spring Boot đáp ứng |
|---|---|
| Giảm cấu hình thủ công | Auto-configuration tạo bean dựa trên classpath và property |
| Chạy nhanh ở máy dev và CI | Máy chủ nhúng (Tomcat mặc định) và java -jar |
| Phiên bản thư viện tương thích | Mỗi bản Boot công bố một danh sách phiên bản đã kiểm chứng (BOM), starter kéo đúng nhóm thư viện |
| Quan sát khi vận hành | Actuator: health, metrics, info |
| Cấu hình theo môi trường | application.yml, profile và biến môi trường |
Cách đọc hình
Đi theo mũi tên liền từ trái sang phải: client gửi HTTP đến Tomcat nhúng, DispatcherServlet tìm controller phù hợp, controller gọi service, service gọi repository, repository truy vấn cơ sở dữ liệu. Toàn bộ khung nét đứt là một tiến trình JVM bạn chạy bằng java -jar. Các hộp màu xanh lá là mã bạn viết; phần còn lại do Spring Boot lắp lúc khởi động. Configuration (application.yml, biến môi trường) quyết định cổng, kết nối cơ sở dữ liệu và nhiều thiết lập khác; Actuator mở các endpoint vận hành như /actuator/health.
Con số đầu tiên: một dự án Boot nặng và chạy thế nào
Lab sinh một dự án mẫu từ Spring Initializr (Maven, Java 17, hai dependency: Web và Actuator), build và chạy:
mvn package (gồm một test khởi động context): 13,6 giây
executable jar: 21.983.613 byte, chứa 42 jar thư viện lồng bên trong
java -jar: Tomcat started on port 18200 (http); Started DemoApplication in 3.984 seconds
GET /actuator/health -> {"groups":["liveness","readiness"],"status":"UP"} [200]
GET /nothing -> {"timestamp":"...","status":404,"error":"Not Found","path":"/nothing"} [404]
Một jar gần 22 MB, một lệnh java -jar, gần bốn giây từ lệnh tới lúc nhận request, và đã có sẵn kiểm tra sức khỏe cùng phản hồi lỗi JSON mà bạn chưa viết dòng nào. Đó là điều "Boot lắp ráp giúp bạn" nghĩa là trong thực tế. Bên trong, Spring giữ khoảng 157 định nghĩa bean ở ứng dụng không web, và 263 định nghĩa (281 singleton) ở ứng dụng web có Actuator trên lab của khóa học (bài IoC Container đo): phần lớn là hạ tầng của Boot, không phải mã của bạn.
Đang tải Animation…
Thử và quan sát
Dự đoán trước: bạn thêm spring-boot-starter-data-jpa và driver cơ sở dữ liệu nhưng chưa khai báo spring.datasource.url; ứng dụng sẽ chạy kèm cảnh báo hay dừng ngay khi khởi động? Bấm Bước tiếp để đi qua bốn bước: starter, classpath, auto-configuration, kết quả. Rồi bật biến thể "thêm data-jpa, thiếu datasource" và so sánh. Điều đáng chú ý là Boot không đoán bừa: khi cấu hình bắt buộc vắng mặt, nó dừng và nói rõ lý do.
Thiếu cấu hình bắt buộc: dừng sớm và rõ ràng
Lab thử đúng tình huống đó với starter JDBC (cùng cơ chế DataSource mà JPA dùng) và driver PostgreSQL nhưng không có spring.datasource.url:
APPLICATION FAILED TO START
---------------------------
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
Báo cáo có ba phần: Description (chuyện gì xảy ra), Reason (nguyên nhân gốc) và Action (làm gì tiếp). Boot dừng với mã thoát 1, không để ứng dụng "chạy nửa vời" rồi lỗi ở request đầu tiên. Cùng nguyên tắc ở mọi bài sau: bean thiếu (bài IoC Container), vòng phụ thuộc (bài Dependency Injection), cổng bị chiếm (bài Chạy ứng dụng) đều cho báo cáo dạng này. Quy tắc đọc: đọc Description và Action trước, không cuộn xuống stack trace.
Lộ trình khóa học
Khóa học đi từ nền tảng đến vận hành: khái niệm cốt lõi của Spring (IoC, DI, bean), tính năng của Spring Boot (auto-configuration, cấu hình), REST API, truy cập dữ liệu với Spring Data JPA, các tính năng nâng cao (lập lịch, email, cache, transaction), microservices, Apache Kafka, AOP và kiểm thử. Mục tiêu không phải thuộc annotation, mà trả lời được khi nhìn một ứng dụng:
- Request đi qua những thành phần nào?
- Bean nào xử lý, và nó được tạo ở đâu?
- Transaction bắt đầu và kết thúc ở đâu?
- Exception được bắt ở đâu?
- Khi cơ sở dữ liệu chậm, điểm nghẽn nằm ở lớp nào?
Cách khóa học làm việc: số liệu đo, không phải lời kể
Mỗi bài từ chương này trở đi đi kèm một dự án thí nghiệm nhỏ (Maven, Spring Boot 4.1.1). Bài trình bày kết quả đã chạy: thông báo lỗi nguyên văn, mã trạng thái HTTP, số lần gọi, thời gian. Khi một thí nghiệm bác bỏ một điều thường được nói (ví dụ "controller prototype không tạo đối tượng mới mỗi request", hay "designator không hỗ trợ ném IllegalArgumentException"), bài ghi điều đo được. Khi một điều không chạy được trong môi trường học (Kubernetes, Gradle, IDE đồ họa), bài nói rõ là chưa chạy. Bạn nên làm lại các thí nghiệm: mỗi bài có phần Bài thực hành với tiêu chí hoàn thành cụ thể và gợi ý nếu kết quả khác.
Hiểu lầm và giới hạn
- Spring Boot không phải một framework thay thế Spring. Nó xây trên Spring Framework; mọi khái niệm bean, proxy,
DispatcherServletvẫn còn nguyên (bài Spring vs Spring Boot chạy một ứng dụng Spring thuần để chứng minh). - Thêm starter "cho chắc" làm classpath phình ra và kích hoạt auto-configuration không cần thiết. Chỉ thêm thứ bạn dùng ngay.
- Không đưa mật khẩu hoặc khóa bí mật vào
application.ymlrồi commit. Dùng biến môi trường hoặc nơi lưu bí mật của hệ thống triển khai. - Boot không làm ứng dụng nhanh hơn khi xử lý request; nó rút ngắn thời gian dựng và cấu hình.
Đọc một ứng dụng Boot lạ trong mười phút
Kỹ năng đáng giá nhất sau bài tổng quan không phải là viết một ứng dụng mới mà là đọc một ứng dụng đã có: bạn nhận một dịch vụ từ đồng nghiệp đã nghỉ việc, không có tài liệu, và cần biết nó làm gì trước khi dám sửa. Một ứng dụng Boot tự mô tả mình qua Actuator, nên mười phút là đủ nếu bạn biết hỏi gì. Lab bật mười hai endpoint (beans, mappings, conditions, env, configprops, startup, health, info, metrics, loggers, threaddump, scheduledtasks) trên chính dự án mẫu của bài này rồi đọc từng cái. Đây là kết quả thật, theo thứ tự nên hỏi:
beans : 272 bean trong context; chỉ 1 bean thuộc mã của bạn (demoApplication)
mappings : 23 ánh xạ trong dispatcherServlet; ngoài /actuator còn /error (hai dạng), tài nguyên tĩnh và /webjars
conditions : 148 điều kiện khớp, 81 không khớp, 11 lớp cấu hình vô điều kiện
env : 8 nguồn thuộc tính (server.ports, commandLineArgs, servletContextInitParams, systemProperties, systemEnvironment, application.properties, applicationInfo, Management Server)
configprops : 24 bean cấu hình có kiểu (server, spring.mvc, management...)
loggers : 402 logger, mức hiệu lực của từng cái
threaddump : 22 luồng (Catalina, Reference Handler, Signal Dispatcher, Notification Thread...)
Con số cho thấy tỉ lệ thật của "mã của bạn" so với "hạ tầng Boot lắp": một ứng dụng chỉ có lớp chính vẫn có 272 bean, trong đó 271 do framework tạo. Vì vậy khi đọc ứng dụng lạ, đừng bắt đầu từ beans: bạn sẽ chìm trong hạ tầng. Hãy đi theo thứ tự câu hỏi sau, mỗi câu có một endpoint trả lời:
- Nó nhận request ở đâu?
mappingsliệt kê mọi đường dẫn cùng lớp và phương thức xử lý. Lọc bỏ/actuator,/errorvà tài nguyên tĩnh, phần còn lại là bề mặt API thật. Với ứng dụng lạ, đây là điểm bắt đầu tốt nhất vì nó trả lời "ứng dụng làm gì" bằng danh sách đường dẫn. - Nó đọc cấu hình từ đâu?
envcho danh sách nguồn thuộc tính theo thứ tự ưu tiên và nguồn nào thắng cho từng khóa (bài Application Properties). Nhìn nguồnConfig resource ... application.propertiesđể biết tệp cấu hình thật, và nguồncommandLineArgs,systemEnvironmentđể biết môi trường ghi đè gì. - Có bộ phận hạ tầng nào bật mà tôi không ngờ?
conditions(Positive matches) cho biết auto-configuration nào đang chạy vì thư viện nào có trên classpath;configpropscho biết các nhóm cấu hình có kiểu. ThấyDataSourceAutoConfigurationhayKafkaAutoConfigurationtrong danh sách nghĩa là ứng dụng nói chuyện với cơ sở dữ liệu hay Kafka dù bạn chưa mở mã. - Nó chạy việc nền không?
scheduledtasksliệt kê tác vụ@Scheduled,threaddumpcho thấy luồng của executor tự tạo. Một luồng lạ tên nhưkafka-coordinator-heartbeathaypool-3-thread-1báo hiệu có tác vụ nền. - Nó chậm ở đâu lúc khởi động?
startup(bài Giới thiệu Spring Boot) chỉ ra bước nào chiếm thời gian. - Nó đang nói gì?
loggerscho mức log từng package; nâng mứcDEBUGtạm thời cho package của ứng dụng để xem luồng thật (bài Actuator).
Hai cảnh báo vận hành. Một: các endpoint này lộ toàn bộ bề mặt tấn công nên chỉ bật chúng ở môi trường dev hoặc sau xác thực và cổng quản trị (bài Actuator đo include=* mở mười chín liên kết). Hai: nếu bạn không được phép bật Actuator trên ứng dụng lạ, vẫn có đường dự phòng: chạy ứng dụng cục bộ với --debug để có báo cáo điều kiện, hoặc đọc pom.xml (starter nào được khai báo là bản đồ thô của khả năng) và application.yml. Thói quen mười phút này đáng hơn mọi bảng thuật ngữ: nó biến "tôi không hiểu dịch vụ này" thành một danh sách câu hỏi có thể trả lời được.
Quyết định thiết kế và đánh đổi
| Quyết định | Khuyến nghị | Đánh đổi |
|---|---|---|
| Bắt đầu một dịch vụ mới | Spring Initializr, Maven hoặc Gradle, Java 17 trở lên | Phụ thuộc quy ước của Boot |
| Số starter | Chỉ những gì dùng ngay | Phải thêm khi cần |
| Cấu hình | Mặc định hợp lý, ghi đè bằng property | Phải học thang ưu tiên |
| Cách chạy | java -jar hoặc image | Không spring-boot:run trên máy chủ |
| Học | Làm lại thí nghiệm | Tốn thời gian hơn đọc |
| Bí mật | Ngoài kho mã | Cần hạ tầng bí mật |
| Khi lỗi | Đọc Description và Action | Cần thói quen |
| Phiên bản | Theo BOM | Theo nhịp của Boot |
Sổ tay xử lý sự cố
- Triệu chứng: ứng dụng dừng ngay sau khi khởi động. Nguyên nhân: cấu hình bắt buộc thiếu hoặc không có thư viện web. Sửa: đọc báo cáo; kiểm tra có
spring-boot-starter-webmvc(bài Công cụ build). - Triệu chứng:
Failed to configure a DataSource. Nguyên nhân: có driver nhưng thiếu URL. Sửa:spring.datasource.urlhoặc đưa cơ sở dữ liệu nhúng vào classpath. - Triệu chứng:
404ở đường dẫn lẽ ra có. Nguyên nhân: controller ngoài cây quét. Sửa: đặt lớp chính ở package gốc (bài Cấu trúc package). - Triệu chứng: chạy ở máy này, lỗi ở máy kia. Nguyên nhân: lệch JDK hay Maven. Sửa: Maven Wrapper và JDK cố định.
- Triệu chứng:
release version 21 not supported. Nguyên nhân: JDK cũ hơn phiên bản Java dự án yêu cầu. Sửa: cài JDK mới hơn hoặc hạjava.version. - Triệu chứng: cổng 8080 bị chiếm. Nguyên nhân: tiến trình khác. Sửa: dừng nó hoặc đổi
server.port. - Triệu chứng: không biết bean nào được tạo. Nguyên nhân: tự động hóa. Sửa:
--debug(bài Auto-configuration). - Triệu chứng: lộ bí mật qua repository. Nguyên nhân: đưa vào
application.yml. Sửa: biến môi trường, secret store, và xoay khóa đã lộ.
Hiệu năng, bảo mật và vận hành
- Thời gian khởi động gồm quét lớp, auto-configuration và tạo bean; bài Giới thiệu Spring Boot đo từng pha.
- Một ứng dụng Boot là một tiến trình đầy đủ: cần giới hạn bộ nhớ, cổng, và quản lý tín hiệu dừng (bài Chạy ứng dụng).
- Mặc định của Boot hợp lý nhưng không phải là chính sách bảo mật của bạn: rà soát endpoint Actuator, thông báo lỗi và cổng lộ ra.
- Cố định phiên bản Java và Maven (wrapper) để build lặp lại được.
- Ghi lại phiên bản Boot và các ghi đè so với BOM (bài Dependency Management).
Bài thực hành
- Kiểm tra môi trường:
java -version(17 trở lên) vàmvn -v(3.6.3 trở lên, hoặc dùng wrapper). - Sinh dự án từ
https://start.spring.iovới Maven, Java 17, dependency Web và Actuator, rồi giải nén. - Chạy
mvn packagevà ghi thời gian, kích thước jar. - Chạy
java -jarvới--server.port=18200; gọi/actuator/healthvà một đường dẫn không tồn tại; ghi hai phản hồi. - Thêm starter JDBC và driver nhưng không khai báo URL; đọc báo cáo lỗi.
- Đọc
pom.xmlcủa dự án sinh ra và liệt kê ba thành phần Boot quản lý phiên bản giúp bạn.
Tiêu chí hoàn thành: bước 3 cho jar vài chục MB và build thành công; bước 4 cho {"groups":["liveness","readiness"],"status":"UP"} và 404 dạng JSON; bước 5 cho Failed to configure a DataSource. Gợi ý nếu bước 4 báo cổng bận: đổi --server.port sang cổng khác và kiểm tra tiến trình cũ đã tắt.
Tự kiểm tra
- Trong hình, thành phần nào do bạn viết, thành phần nào do Spring Boot lắp?
- Vì sao thiếu
spring.datasource.urllàm ứng dụng dừng thay vì chạy tiếp? - Bạn muốn đổi cổng từ 8080 sang 9090 mà không sửa mã. Bạn đặt thiết lập ở đâu?
- Báo cáo
APPLICATION FAILED TO STARTđọc theo thứ tự nào? - Vì sao nói một ứng dụng Boot là một tiến trình JVM chứa cả máy chủ web?
- Boot có làm request xử lý nhanh hơn không?
- Vì sao không thêm starter "cho chắc"?
- Vì sao khóa học nhấn mạnh làm lại thí nghiệm?
Gợi ý đáp án. (1) Controller, service, repository do bạn viết; Tomcat, DispatcherServlet, Jackson, Actuator do Boot lắp. (2) Bean DataSource cần URL; thiếu URL thì việc tạo bean thất bại và Boot dừng sớm để bạn sửa. (3) server.port=9090 trong application.yml, hoặc biến môi trường SERVER_PORT=9090, hoặc --server.port=9090. (4) Description và Action trước, rồi Reason; không bắt đầu từ stack trace. (5) Jar chứa Tomcat nhúng nên chạy bằng java -jar không cần máy chủ cài sẵn. (6) Không; nó rút ngắn thời gian dựng và cấu hình. (7) Classpath phình ra và kích hoạt auto-configuration không cần thiết. (8) Con số và thông báo đo được tin cậy hơn lời kể, và thí nghiệm bác bỏ được những điều tưởng đúng.
Điều cần nhớ
- Spring Boot lắp ráp ứng dụng chạy được từ starter, auto-configuration và cấu hình.
- Một ứng dụng Boot là một tiến trình JVM chứa cả máy chủ web, đóng gói thành một jar thực thi.
- Mặc định có thể bị thay bằng cấu hình hoặc bean của bạn.
- Thiếu cấu hình bắt buộc làm ứng dụng thất bại sớm và rõ ràng, với báo cáo có Description, Reason, Action.
- Bài sau xem
@SpringBootApplicationvàSpringApplication.runthực sự làm gì.
Học liệu chọn lọc
- Spring Boot Reference: System Requirements
- Spring Boot Reference: Build Systems
- Spring Initializr
- Spring Boot Reference: Getting Started
- Spring Boot 4.0 Migration Guide
- The Twelve-Factor App
- Java SE Documentation (Oracle): the java command
- Sách: Spring Boot in Action (Craig Walls); Spring in Action (Craig Walls).