Tự Viết Custom Starter: Cấu Trúc 2 Module Và Supply Chain
Hướng dẫn viết Spring Boot Starter nội bộ theo mô hình 2 module, đăng ký AutoConfiguration.imports, đặt tên chuẩn và quét an toàn chuỗi cung ứng.
TL;DR: Một Custom Starter chuẩn mực gồm 2 module riêng biệt: *-spring-boot-autoconfigure (chứa mã Java @AutoConfiguration và @ConfigurationProperties) và *-spring-boot-starter (tệp JAR rỗng kéo theo module autoconfigure cùng runtime library). Spring Boot 2.7+ phát hiện lớp cấu hình tự động thông qua tệp META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. Quy ước đặt tên yêu cầu các starter bên thứ ba hoặc nội bộ phải đặt tên theo định dạng X-spring-boot-starter để tránh nhầm lẫn với các starter chính thức của Pivotal.
Tự đóng gói starter nội bộ giúp doanh nghiệp tái sử dụng mã nguồn hạ tầng, chuẩn hóa cấu hình phân tán và quản lý lỗ hổng bảo mật chuỗi cung ứng tập trung.
1. Tại sao bắt buộc phải tách Starter thành 2 module riêng biệt?
Nhiều lập trình viên thường gộp toàn bộ mã cấu hình và khai báo thư viện vào một module duy nhất. Tuy nhiên, việc tách làm 2 module là quy chuẩn thiết kế bắt buộc vì hai lý do kỹ thuật:

- Kiểm soát đồ thị phụ thuộc (Dependency Graph): Một số dịch vụ chỉ muốn lấy logic cấu hình tự động mà đã có sẵn thư viện runtime từ nguồn khác. Việc tách module cho phép họ chỉ nạp
autoconfiguremà không kéo thêm các dependency trùng lặp. - Khai báo phụ thuộc tùy chọn (Optional Dependency): Trong module
autoconfigure, thư viện cốt lõi được đánh dấu<optional>true</optional>. Khi đó,@ConditionalOnClasssẽ kiểm tra chính xác sự có mặt của class trên classpath. Modulestarterđóng vai trò kéo cả hai về cùng một lúc cho người dùng ứng dụng cuối.
2. Spring Boot tìm nạp cấu hình qua AutoConfiguration.imports ra sao?
Từ phiên bản Spring Boot 2.7 trở đi, cơ chế nạp cấu hình tự động chuyển hoàn toàn sang tệp:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Khi ứng dụng khởi động, bộ xử lý AutoConfigurationImportSelector duyệt qua toàn bộ các tệp JAR trên classpath để tìm kiếm tệp tin này. Nội dung bên trong là danh sách các lớp cấu hình đầy đủ (Fully Qualified Class Name):
com.acme.tracing.TracingAutoConfiguration
Nếu thiếu tệp khai báo này trong thư mục resources, Spring Boot sẽ hoàn toàn không nhận diện được lớp cấu hình, dù bạn có gắn đầy đủ các annotation @AutoConfiguration.
3. Các bước xây dựng Custom Starter hoàn chỉnh gồm những gì?
Giả sử doanh nghiệp cần xây dựng bộ starter acme-tracing để tự động cấu hình distributed tracing cho mọi microservice.
Bước 1: Khai báo TracingProperties
Sử dụng @ConfigurationProperties để ràng buộc các tham số cấu hình từ file YAML vào POJO:
@ConfigurationProperties(prefix = "acme.tracing")
@Validated
public class TracingProperties {
private boolean enabled = true;
@NotBlank
private String endpoint = "https://tracing.acme.internal";
@NotBlank
private String serviceName;
// Getters va Setters
}
Bước 2: Viết lớp TracingAutoConfiguration
Xây dựng logic nạp bean có điều kiện kèm theo các giá trị mặc định:
@AutoConfiguration
@ConditionalOnClass(Tracer.class)
@EnableConfigurationProperties(TracingProperties.class)
@ConditionalOnProperty(name = "acme.tracing.enabled", havingValue = "true", matchIfMissing = true)
public class TracingAutoConfiguration {
@Bean
@ConditionalOnMissingBean // Nhuong quyen khi nguoi dung tu khai bao Tracer
public Tracer tracer(TracingProperties properties) {
return Tracer.builder()
.endpoint(properties.getEndpoint())
.serviceName(properties.getServiceName())
.build();
}
}
Bước 3: Đăng ký imports và đóng gói Starter
- Thêm tên class
com.acme.tracing.TracingAutoConfigurationvào fileAutoConfiguration.imports. - Khởi tạo module
acme-tracing-spring-boot-starterlà một file POM rỗng chỉ chứa dependency kéoacme-tracing-spring-boot-autoconfigurevàacme-tracing-core.
4. Quy ước đặt tên Naming Convention đảo Namespace có ý nghĩa gì?
Tài liệu chính thức của Spring Boot đưa ra quy tắc đặt tên nghiêm ngặt:
| Phân loại | Quy ước đặt tên | Ví dụ thực tế |
|---|---|---|
| Starter chính thức từ Spring | spring-boot-starter-{name} | spring-boot-starter-web |
| Starter bên thứ ba hoặc nội bộ | {name}-spring-boot-starter | acme-tracing-spring-boot-starter |
Việc đảo ngược vị trí đặt tên nhằm bảo vệ người dùng, tránh nhầm lẫn rằng thư viện đó do nhóm phát triển Spring chính thức hỗ trợ và cam kết vá lỗi bảo mật (SLA).
5. Kiểm soát an toàn chuỗi cung ứng (Supply Chain Security) như thế nào?
Mỗi starter kéo theo hàng chục thư viện phụ thuộc gián tiếp. Sự cố Log4Shell (CVE-2021-44228) đã chứng minh rằng một lỗ hổng trong dependency transit có thể đánh sập toàn bộ hệ thống.
Tự động quét CVE bằng OWASP Dependency-Check
Cấu hình plugin trong Maven để dừng tiến trình build nếu phát hiện lỗ hổng có điểm CVSS trên 7.0:
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>10.0.4</version>
<configuration>
<failBuildOnCVSS>7.0</failBuildOnCVSS>
</configuration>
</plugin>
Quản lý SBOM (Software Bill of Materials)
Từ Spring Boot 3.3+, ứng dụng hỗ trợ tạo danh mục thành phần phần mềm (SBOM) chuẩn CycloneDX thông qua Actuator:
management:
endpoints:
web:
exposure:
include: sbom
Endpoint /actuator/sbom cung cấp danh sách đầy đủ toàn bộ thư viện đang chạy tại runtime, phục vụ công tác thanh tra bảo mật và phản ứng sự cố nhanh chóng.
6. Cơ chế nhường chỗ @ConditionalOnMissingBean vận hành ra sao?
Điểm tinh tế nhất của Custom Starter là không bao giờ tranh chấp quyền điều khiển với người dùng. Khi bạn gắn @ConditionalOnMissingBean trên phương thức tạo Tracer, Spring Boot sẽ kiểm tra xem ApplicationContext đã tồn tại bean kiểu Tracer chưa.
Nếu người dùng tự định nghĩa một Tracer tùy chỉnh trong mã nguồn của họ, starter sẽ tự động rút lui hoàn toàn. Điều này đảm bảo thư viện nội bộ luôn cung cấp giá trị mặc định tối ưu nhưng không bao giờ gây lỗi xung đột trùng bean.
Tóm tắt
- Custom Starter bắt buộc tách làm 2 module:
autoconfigurechứa mã cấu hình vàstarterchỉ là POM rỗng kéo phụ thuộc. - File
META-INF/spring/...AutoConfiguration.importslà cổng giao tiếp duy nhất để Spring Boot phát hiện lớp cấu hình tự động. - Starter nội bộ phải đặt tên dạng
{name}-spring-boot-starterđể phân định rõ ràng với thư viện chính thức của Spring. - Sử dụng
@ConditionalOnMissingBeanđể đảm bảo starter luôn nhường quyền kiểm soát cho lập trình viên ứng dụng cuối. - Kiểm soát rủi ro an ninh chuỗi cung ứng thông qua OWASP Dependency-Check và trích xuất SBOM từ Spring Boot Actuator.
Tự kiểm tra
- Q1Tại sao một Custom Starter chuẩn của Spring Boot luôn phải tách thành 2 module riêng biệt (autoconfigure và starter)?
- Q2Tệp tin nào sau đây là entry point bắt buộc trên Spring Boot 2.7+ để container nhận diện các lớp Auto-configuration?
- Q3Theo quy ước của Spring Boot, một starter do doanh nghiệp Acme tự phát triển nên đặt tên theo định dạng nào?
- Q4Tại sao trong module autoconfigure, dependency của thư viện nghiệp vụ cốt lõi lại nên được gắn thẻ optional=true?
- Q5Endpoint nào của Spring Boot Actuator (từ bản 3.3+) cung cấp danh sách thành phần phần mềm (SBOM) chuẩn định dạng CycloneDX?
Bài tiếp theo: @EnableAutoConfiguration: Cơ Chế Đọc File Imports
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.
Starter & BOM: Quản Lý Dependency Đồng Bộ Trong Maven
Bài tiếp@EnableAutoConfiguration: Cơ Chế Đọc File Imports
Lộ trình Junior Java Backend Developer
✓ Bạn vừa đi hết chặng Maven / Gradle — build & dependency management (8/21)
Chặng tiếp: HTTP, REST & web cho backend — “Module 2, HTTP & Web: tổng quan”
Ô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