Starter & BOM: Quản Lý Dependency Đồng Bộ Trong Maven
Tìm hiểu cấu trúc jar rỗng của Starter, vai trò của BOM spring-boot-dependencies và cơ chế dependencyManagement ghi đè giải thuật nearest-wins.
TL;DR: Starter là một tệp JAR hoàn toàn không chứa file bytecode .class — bên trong chỉ có tệp pom.xml liệt kê các transitive dependencies cần thiết cho một tính năng. BOM (spring-boot-dependencies) là một POM file chứa thẻ <dependencyManagement> xác định phiên bản tương thích đã được kiểm thử cho hơn 200 thư viện phổ biến. Cơ chế <dependencyManagement> của BOM ghi đè hoàn toàn giải thuật Nearest-Wins của Maven, xóa bỏ triệt để xung đột phiên bản mà không cần khai báo <version> thủ công.
Nắm vững nguyên lý hoạt động của Starter và BOM giúp bạn tự tin quản lý dependency, hoán đổi linh hoạt các module nhúng và tự tạo starter nội bộ cho doanh nghiệp.
1. Nạn Dependency Hell của Spring 4 từng gây khó khăn thế nào?
Trước khi có Spring Boot, việc bổ sung tính năng Web MVC đòi hỏi lập trình viên phải tự chọn và khai báo từng thư viện riêng rẽ:
<!-- Thoi ky Spring 4: Phai tu chon va doi chieu tung version -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
Vấn đề nan giải nhất là tính tương thích: liệu jackson-databind 2.9.0 có chạy ổn định với spring-webmvc 4.3.9 không? Mỗi lần nâng cấp phiên bản thư viện là một lần đối mặt với nguy cơ lỗi NoSuchMethodError hay ClassNotFoundException lúc runtime.
2. Bản chất vật lý của một gói Spring Boot Starter là gì?
Nếu bạn tải tệp spring-boot-starter-web-3.4.0.jar từ Maven Central và giải nén, bạn sẽ thấy một cấu trúc bất ngờ:
spring-boot-starter-web-3.4.0.jar
└── META-INF/
├── MANIFEST.MF
└── maven/org.springframework.boot/spring-boot-starter-web/
├── pom.properties
└── pom.xml <-- File duy nhat co y nghia
Gói JAR này hoàn toàn không chứa bất kỳ file .class nào. Nó chỉ đóng vai trò một bản kê khai các thư viện cần thiết:
<project>
<artifactId>spring-boot-starter-web</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
</project>
Tất cả các thẻ dependency bên trong starter đều không khai báo <version>. Mục đích duy nhất của nó là kéo theo khoảng 30 thư viện liên quan chỉ với một dòng khai báo duy nhất.
3. BOM giải quyết xung đột phiên bản thông qua cơ chế nào?
Các phiên bản thư viện thực chất được định nghĩa trong BOM (Bill of Materials) mang tên spring-boot-dependencies. BOM là một file POM chỉ chứa thẻ <dependencyManagement>:
<!-- spring-boot-dependencies (POM thuan) -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.0</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
Hai cách tích hợp BOM vào dự án:
- Sử dụng parent (Khuyến nghị cho ứng dụng độc lập):
XML
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.0</version> </parent> - Sử dụng scope import (Bắt buộc khi dự án đã kế thừa corporate parent khác):
XML
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.4.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
4. Bóc tách spring-boot-starter-web gồm những thành phần gì?
Một khai báo spring-boot-starter-web sẽ tự động kéo theo 4 cụm thư viện chính:
| Cụm thư viện | Các tệp JAR tiêu biểu | Trách nhiệm đảm nhận |
|---|---|---|
spring-boot-starter | spring-boot, autoconfigure, logback, snakeyaml | Động cơ khởi động, auto-config, logging và YAML parser |
spring-boot-starter-json | jackson-databind, jackson-datatype-jsr310 | Tuần tự hóa JSON, hỗ trợ kiểu thời gian Java 8 Date/Time |
spring-boot-starter-tomcat | tomcat-embed-core, tomcat-embed-websocket | Máy chủ Servlet nhúng trực tiếp trong ứng dụng |
spring-webmvc | spring-web, spring-webmvc, spring-aop | DispatcherServlet, Controller và các bộ chuyển đổi bản tin |
5. Làm thế nào để loại trừ và hoán đổi máy chủ nhúng qua Exclusions?
Nhờ cấu trúc transitive, bạn có thể dễ dàng thay đổi máy chủ web mà không cần sửa đổi bất kỳ dòng mã Java nào:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Spring Boot sẽ phát hiện jetty-server trên classpath và tự động khởi tạo JettyServletWebServerFactory thay cho Tomcat.
Nếu bạn loại trừ Tomcat mà quên khai báo một server nhúng thay thế (như Jetty hoặc Undertow), dự án vẫn build Maven thành công nhưng sẽ sập ngay lúc khởi động với lỗi NoSuchBeanDefinitionException vì thiếu ServletWebServerFactory.
6. Tại sao BOM lại đánh bại được giải thuật Nearest-Wins của Maven?
Trong Maven thông thường, nếu dependency A đòi jackson 2.14 và dependency B đòi jackson 2.18, Maven áp dụng giải thuật Nearest-Wins (ưu tiên dependency nằm ở tầng nông hơn trong cây phân cấp), bất chấp tính tương thích:

BOM giải quyết triệt để rủi ro này vì Maven luôn duyệt qua thẻ <dependencyManagement> trước khi phân giải cây phụ thuộc. Phiên bản được định nghĩa trong BOM sẽ luôn chiến thắng và khóa cố định phiên bản cho toàn bộ dự án.
7. Những cạm bẫy phiên bản phổ biến nhất khi sử dụng Starter?
- Tự ý ghi đè version của Starter: Khai báo
<version>thủ công trên các starter của Spring Boot sẽ phá vỡ tính đồng bộ của BOM và dẫn đến lỗi không tương thích runtime. - Nhầm lẫn giữa Starter và Auto-configuration: Starter chỉ là cơ chế đóng gói Maven để kéo file JAR. Còn Auto-configuration là mã nguồn Java bên trong
spring-boot-autoconfigurethực hiện việc đăng ký bean vào container.
Tóm tắt
- Spring Boot Starter là các tệp JAR rỗng không chứa class, chỉ dùng để liệt kê transitive dependencies theo cụm tính năng.
- BOM (
spring-boot-dependencies) định nghĩa phiên bản an toàn cho hơn 200 thư viện qua<dependencyManagement>. - Cơ chế
<dependencyManagement>của BOM luôn ghi đè giải thuật Nearest-Wins của Maven để đảm bảo tính đồng nhất. - Khi dự án đã có corporate parent, hãy tích hợp BOM bằng thẻ
<dependency>vớitype=pomvàscope=import. - Kỹ thuật Exclusions cho phép hoán đổi các thành phần hạ tầng (như Tomcat sang Jetty) hoàn toàn thông qua cấu hình Maven.
Tự kiểm tra
- Q1Bản chất kỹ thuật bên trong của một file Spring Boot Starter JAR (như spring-boot-starter-web) là gì?
- Q2Cơ chế nào giúp BOM (spring-boot-dependencies) ngăn chặn lỗi xung đột phiên bản giữa các thư viện?
- Q3Trong trường hợp dự án công ty bắt buộc phải kế thừa acme-parent, làm thế nào để bạn sử dụng được BOM của Spring Boot?
- Q4Điều gì xảy ra khi bạn exclude starter-tomcat khỏi starter-web nhưng lại quên khai báo starter-jetty hoặc starter-undertow thay thế?
- Q5Tại sao lập trình viên KHÔNG nên khai báo thẻ version cho các starter của Spring Boot trong file pom.xml?
Bài tiếp theo: Tự Viết Custom Starter Chuẩn Production
Bài này có giúp bạn hiểu bản chất không?
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.
Spring Boot Là Gì: 5 Trụ Cột Nền Tảng Và Vòng Đời Startup
Bài tiếpTự Viết Custom Starter: Cấu Trúc 2 Module Và Supply Chain
Lộ trình Junior Java Backend Developer
Chặng 8/21 · Maven / Gradle — build & dependency management — bài 1/2 của chặ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.
Thảo luận & góp ý
Hỏi đáp về bài này