Spring Core & Boot/Spring Boot là gì — tại sao ra đời và 5 trụ cột
27/41
Bài 27 / 41~12 phútSpring Boot & Auto-configurationMiễn phí lượt xem

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.0spring-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:

Đối chiếu hai cột: cột trái là việc tay phải làm ở mọi project Spring 4 gồm tự chọn version cho 15+ module, viết applicationContext.xml, viết web.xml và cài Tomcat rời, viết log4j.xml cùng health check, đóng WAR phụ thuộc version Tomcat của máy đích; cột phải là trụ cột Boot xoá đúng việc đó theo từng hàng gồm Starter cộng BOM, Auto-Configuration, server nhúng, Actuator cộng config đưa ra ngoài jar, build plugin sinh jar chạy bằng java -jar

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}.yml toggle 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:

Trục thời gian của SpringApplication.run gom 14 bước thành năm mốc: bước 1 đến 7 Boot nhận diện kiểu app và dựng Environment, bước 8 đến 11 tạo ApplicationContext và chạy Initializer, bước 12 là refresh của Spring core tạo mọi singleton, bước 13 CommandLineRunner, bước 14 ApplicationReadyEvent; ba dòng log console dán vào đúng mốc sinh ra chúng

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 ConfigurationCó default thông minh; override chỉ khi cần khác
No code generationKhông sinh source code — chỉ register bean runtime
No XML requiredMọ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 opinionatedLock-in
Override defaultĐăng ký bean của bạn — Boot tự rútKhô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 BootCode Spring core vẫn chạy, chỉ bỏ Boot dependencyKhó — 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: @SpringBootApplication là 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*, file AutoConfiguration.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

Tự kiểm tra
0/5 câu đã trả lời
  1. Q1
    Spring 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ể.
  2. 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?
  3. Q3
    Vì 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ó?
  4. Q4
    @SpringBootApplication là 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.
  5. Q5
    Mộ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ủa DataSourceAutoConfiguration? 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

Đặt 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

Bài tiếp theo

Spring Boot Starter & BOM — hết dependency hell