Spring Boot là gì — tại sao ra đời và 5 trụ cột
Spring Boot không phải framework mới mà là 5 lớp đóng gói trên Spring Framework để xoá 60-90 phút boilerplate setup mỗi project. Bài này giải thích bài toán Spring 4, 5 trụ cột Boot, vòng đời SpringApplication.run() kết thúc bằng refresh(), và tại sao 'opinionated' không phải 'lock-in'.
TL;DR: Spring Boot không phải framework mới — là 5 lớp đóng gói trên Spring Framework ra đời 2014 để xoá 60-90 phút boilerplate setup mỗi project Spring 4 (web.xml, applicationContext.xml, Tomcat external, log4j.xml). 5 trụ cột: Starter Dependencies (BOM curate 200+ lib version), Auto-Configuration (143 autoconfig class register bean conditional), Embedded Server (Tomcat trong jar), Production-ready Features (Actuator + externalized config), Build Plugin (executable fat jar). SpringApplication.run() làm 14 bước, bước 12 là refresh() của Spring core — phần còn lại là Boot wrap thêm. "Opinionated" không phải lock-in: mọi default đều override được qua @ConditionalOnMissingBean.
Bài này đặt nền cho cả module: trả lời vì sao Boot tồn tại trước khi bài 02-06 bóc từng trụ cột. Sau bài này, bạn không nhìn @SpringBootApplication như "magic" nữa — bạn biết nó kích hoạt đúng cái gì.
1. Bài toán Spring 4 (2014) — vì sao Boot ra đời
Đầu 2014, mỗi project Spring 4 mới phải làm 5 thao tác y hệt:
Khai báo dependency thủ công — 15+ module Spring với version riêng, mỗi lần upgrade phải kiểm tra compatibility matrix tay:
<!-- pom.xml Spring 4 -- hang chuc dong -->
<properties>
<spring.version>4.0.0</spring.version>
<hibernate.version>4.3.0</hibernate.version>
<jackson.version>2.3.0</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 13 dependency nua, moi dependency mot version -->
</dependencies>
Pain cụ thể: spring-web 4.0.0 và spring-context 4.0.5 không tương thích — developer phải biết điều này theo cách thủ công.
Viết web.xml — khai báo ContextLoaderListener, DispatcherServlet, servlet mapping, 30-50 dòng XML dễ typo, IDE không validate runtime behavior.
Viết applicationContext.xml — DataSource, EntityManagerFactory, TransactionManager, component-scan, mỗi project copy từ project trước rồi chỉnh tay.
Cài Tomcat external — download từ apache.org, copy WAR vào webapps/, startup.sh. Version Tomcat trên dev local và prod server dễ skew.
Tạo log4j.xml, persistence.xml, ... — mỗi file 20-50 dòng config mặc định, lặp ở mọi project.
Tổng cộng: 60-90 phút setup hoàn toàn boilerplate cho project Spring 4 mới. Pivotal gọi đây là "snowflake projects" — mỗi project là một bông tuyết khác nhau, không reproducible.
2. Spring Boot 1.0 (tháng 4/2014) — một ý tưởng đơn giản
Phil Webb và Dave Syer ở Pivotal đề xuất: gom 5 thao tác trên thành defaults thông minh, developer chỉ override khi cần khác default.
Cùng app web với Spring Boot 1.0:
// Toan bo Java code -- 7 dong
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
<!-- pom.xml -- 6 dong -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- KHONG co <version> -->
</dependency>
</dependencies>
mvn spring-boot:run # Tomcat embedded tu start, port 8080
13 dòng tổng, không XML, không Tomcat external. Đây là sự thay đổi paradigm.
3. 5 trụ cột của Spring Boot
Spring Boot đóng gói 5 thứ. Đây không phải danh sách tính năng rời nhau — mỗi trụ cột xoá đúng 1 thao tác tay ở section 1:

3.1 Starter Dependencies — giải quyết pain version mismatch
Bài toán: 15+ module Spring với version riêng, upgrade = kiểm tra compatibility matrix tay.
Lời giải: Pivotal duy trì 1 BOM (Bill of Materials — tài liệu liệt kê version đã kiểm tra tương thích) cho 200+ thư viện phổ biến: Spring, Hibernate, Jackson, Tomcat, HikariCP, Logback, Mockito, ...
Bạn khai báo spring-boot-starter-parent → kế thừa BOM → khai báo dependency không cần <version>:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<!-- Boot BOM resolve version -- khong can khai bao -->
</dependency>
Upgrade Boot 3.4 lên 3.5 = đổi 1 dòng parent version → tất cả 200 lib upgrade nhất quán. Bài 02 — Starter & BOM bóc sâu cơ chế.
3.2 Auto-Configuration — giải quyết pain applicationContext.xml
Bài toán: copy 30-100 dòng XML config cho mỗi project.
Lời giải: Boot có 143 auto-config class (Boot 3.4). Mỗi class tự register bean có điều kiện dựa trên classpath + property:
// DataSourceAutoConfiguration.java (rut gon)
@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties props) {
return DataSourceBuilder.create()
.url(props.getUrl())
.username(props.getUsername())
.password(props.getPassword())
.build();
}
}
Logic: có DataSource.class trên classpath AND user chưa tự register DataSource bean (@ConditionalOnMissingBean) → Boot register bean mặc định. Bạn chỉ set property:
spring.datasource.url=jdbc:postgresql://localhost:5432/app
spring.datasource.username=app
spring.datasource.password=secret
Override bằng cách tự khai báo @Bean DataSource — Boot tự rút lui. Bài 03 — Auto-Configuration deep dive mổ cơ chế @ConditionalOn*.
3.3 Embedded Server — giải quyết pain Tomcat external
Bài toán: cài Tomcat tách rời, deploy WAR, version skew giữa dev và prod.
Lời giải: Boot pull Tomcat (hoặc Jetty/Undertow/Netty) như library trong jar:
mvn package
# Tao target/app.jar -- KHONG phai .war
java -jar target/app.jar
# Tomcat tu start, port 8080
SpringApplication.run() detect tomcat-embed-core trên classpath → tạo TomcatServletWebServerFactory → khởi động Tomcat → register DispatcherServlet programmatically (thay web.xml). Chi tiết bước này nằm trong vòng đời ở section 4.
Lợi ích: 1 artifact duy nhất (1 jar = mọi thứ cần), reproducible (Tomcat version khớp giữa dev và prod), deploy anywhere (Docker, K8s, Lambda).
3.4 Production-ready Features — giải quyết pain config lặp
Bài toán: mọi project cần health check, metrics, log config — viết lại từ đầu.
Lời giải:
- Actuator: 15+ endpoint sẵn có (
/actuator/health,/actuator/metrics,/actuator/startup). Thêm 1 starter là có ngay. - Externalized Configuration: 14 nguồn property theo thứ tự ưu tiên — env var, command-line arg,
application.yml, profile, v.v. - Logging: Logback default với pattern Boot tinh chỉnh sẵn.
- Profiles:
application-{profile}.ymltoggle cấu hình dev/staging/prod.
Bài 04 — Externalized Configuration và bài 05 — Profiles đào sâu.
3.5 Build Plugin — giải quyết pain deploy artifact
spring-boot-maven-plugin đóng gói app thành executable fat jar — jar chứa Main-Class manifest và toàn bộ dependency jar lồng nhau:
mvn spring-boot:run # Dev mode, hot reload voi devtools
mvn spring-boot:build-image # Build Docker image qua Buildpacks, khong can Dockerfile
mvn package # Tao executable jar
Executable jar: java -jar app.jar tự khởi động Tomcat + app, không cần Tomcat external.
4. SpringApplication.run() làm gì — kết thúc bằng refresh()
Đây là cơ chế cốt lõi: khi bạn gọi SpringApplication.run(App.class, args), Boot thực hiện 14 bước:

Bước 12 (refresh()) chính là 12 bước của Spring core mà bạn đã học ở bài 02 — refresh() lifecycle: scan component, load BeanDefinition, instantiate singleton, gọi @PostConstruct, publish ContextRefreshedEvent. Boot wrap thêm 11 bước trước (detect type, setup environment, tạo context, ...) và 2 bước sau (CommandLineRunner, ApplicationReadyEvent).
Hiểu điều này giúp debug startup: nếu app chậm ở "Tomcat initialized with port 8080" → bước 12 (refresh()) chậm → bean nào đó khởi tạo lâu (DataSource connect, Hibernate validate schema). Nếu chậm sau "Started App" → CommandLineRunner chậm.
Log console tương ứng từng bước:
2026-04-15 10:00:00 INFO com.olhub.App : Starting App using Java 21
# ^ Buoc 8 -- ApplicationStartingEvent
2026-04-15 10:00:01 INFO o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080
# ^ Ben trong buoc 12 (refresh -> onRefresh -> TomcatWebServer.start())
2026-04-15 10:00:02 INFO com.olhub.App : Started App in 2.345 seconds (process running for 2.8)
# ^ Ket thuc buoc 12
2026-04-15 10:00:02 INFO com.olhub.App : Application is ready
# ^ Buoc 14 -- ApplicationReadyEvent
Annotation @SpringBootApplication là composite của 3 annotation:
@SpringBootConfiguration // = @Configuration: class nay la bean config
@EnableAutoConfiguration // bat 143 autoconfig class
@ComponentScan // scan @Component/@Service/@Repository trong package hien tai
public @interface SpringBootApplication { ... }
Và annotation được khai báo từ Resource & @SpringBootApplication — ba annotation đó kích hoạt đúng luồng refresh() ở trên.
5. "Opinionated" — không phải lock-in
Critic của Boot hay nói: "opinionated" nghe như framework ép buộc. Không phải vậy. Spring Boot có 3 nguyên tắc thiết kế:
| Nguyên tắc | Ý nghĩa |
|---|---|
| Convention over Configuration | Có default thông minh; override chỉ khi cần khác |
| No code generation | Không sinh source code — chỉ register bean runtime |
| No XML required | Mọi config qua properties hoặc Java |
Vì sao chọn "opinionated"? Pivotal nhận ra 80% project dùng cùng stack (Tomcat + HikariCP + Hibernate + Logback). Viết default cho 80% đó, để developer tập trung vào 20% business logic khác biệt. Nếu default không phù hợp, override nó — cơ chế chính thức là @ConditionalOnMissingBean: Boot tự rút lui khi bạn đăng ký bean cùng kiểu.
Opinionated khác lock-in như thế nào:
| Boot opinionated | Lock-in | |
|---|---|---|
| Override default | Đăng ký bean của bạn — Boot tự rút | Không có cơ chế override chính thức |
| Bỏ feature | @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) | Thường phải rebuild/fork framework |
| Migrate ra khỏi Boot | Code Spring core vẫn chạy, chỉ bỏ Boot dependency | Khó — bị bind vào API độc quyền |
// Overide DataSource default -- Boot tu rut lui
@Configuration
public class CustomDataSourceConfig {
@Bean // user dang ky DataSource
public DataSource dataSource() {
var ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:postgresql://custom-host/db");
ds.setMaximumPoolSize(50);
return ds;
}
// DataSourceAutoConfiguration thay @ConditionalOnMissingBean gap DataSource ban vua khai bao
// -> tu skip, khong register bean mac dinh
}
// Tat han 1 autoconfig
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class App { ... }
Boot đẹp ở chỗ: bạn dùng nó để bootstrap nhanh, nhưng vẫn đào sâu tới Spring core khi cần. Mọi autoconfig là Java code đọc được — không có "magic curtain".
Liên hệ các bài khác
Bài này là nền cho cả module. Ghép với các bài khác để thấy bức tranh hoàn chỉnh:
- refresh() — 12 bước khởi tạo container: bước 12 của
SpringApplication.run()chính làrefresh()đó — Boot wrap thêm 13 bước xung quanh. Đọc bài này để hiểu chi tiết từng pha bên trong bước 12. - Resource & @SpringBootApplication:
@SpringBootApplicationlà composite annotation; bài đó giải thích vì sao annotation này đủ kích hoạt toàn bộ container. - Starter & BOM: trụ cột 1 (Starter Dependencies) — bóc sâu BOM,
spring-boot-starter-parent, cách Boot quản version 200 lib và cách override khi cần. - Auto-Configuration deep dive: trụ cột 2 — cơ chế
@ConditionalOn*, fileAutoConfiguration.imports, thứ tự ưu tiên autoconfig.
Tóm tắt
- Spring Boot không phải framework mới — là 5 lớp đóng gói trên Spring Framework để xoá 60-90 phút boilerplate.
- 5 trụ cột: Starter Dependencies (BOM 200+ lib), Auto-Configuration (143 class,
@ConditionalOn*), Embedded Server (Tomcat trong jar), Production-ready Features (Actuator + config), Build Plugin (executable fat jar). SpringApplication.run()làm 14 bước: bước 12 làrefresh()của Spring core — Boot wrap 11 bước trước + 2 bước sau.- "Opinionated": có default thông minh + cơ chế override formal (
@ConditionalOnMissingBean). Khác lock-in ở chỗ override là tính năng thiết kế, không phải hack. - Debug startup: "Starting App" = bước 8, "Tomcat initialized" = giữa bước 12 (
refresh()), "Started App" = cuối bước 12, "Application ready" = bước 14.
Tự kiểm tra
- Q1Spring Boot 3.x được giới thiệu là "opinionated". Hãy giải thích sự khác biệt giữa "opinionated" và "lock-in" theo cơ chế
@ConditionalOnMissingBean. Cho 1 ví dụ override cụ thể. - Q2
SpringApplication.run(App.class, args)thực hiện 14 bước. Bước nào là trọng tâm? Vì sao khi app khởi động chậm, bước đó thường là nguyên nhân? - Q3Vì sao Spring Boot ra đời năm 2014 giải quyết được "snowflake project" problem? "Snowflake project" là gì và Boot dùng cơ chế nào để xoá nó?
- Q4
@SpringBootApplicationlà composite của 3 annotation. Liệt kê 3 annotation đó và giải thích vai trò của từng cái trong việc kích hoạt Spring container. - Q5Một developer viết Boot app và thêm
@Bean public DataSource ds() {...}trong class có@SpringBootApplication. App có DataSource nào được dùng: của developer hay củaDataSourceAutoConfiguration? Giải thích theo cơ chế@ConditionalOnMissingBean.
Bài tiếp theo: Starter & BOM
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Ôn phỏng vấn
Bài này trả lời được các câu phỏng vấn sau — tự trả lời thử trước khi mở đáp án.
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên