Spring Core & Boot
29/41
Bài 29 / 41~12 phútSpring Boot & Auto-configurationLộ trình · chặng 08/21Miễn phí… lượt xem

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:

Đối chiếu hai phương án đóng gói lib acme-tracing-core: cột trái gộp làm một module thì hoặc khai core bắt buộc khiến mọi consumer bị kéo theo core kể cả service không dùng tracing, hoặc khai core optional nên consumer không có core và điều kiện luôn false im lặng; cột phải tách hai module thì autoconfigure giữ logic và khai core optional, còn starter là jar rỗng pull explicit cả hai, nhờ đó consumer khai đúng một dependency mà điều kiện classpath vẫn có nghĩa

  1. 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 autoconfigure mà không kéo thêm các dependency trùng lặp.
  2. 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 đó, @ConditionalOnClass sẽ kiểm tra chính xác sự có mặt của class trên classpath. Module starter đó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:

Plain Text
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):

Plain Text
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:

Java
@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:

Java
@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.TracingAutoConfiguration vào file AutoConfiguration.imports.
  • Khởi tạo module acme-tracing-spring-boot-starter là một file POM rỗng chỉ chứa dependency kéo acme-tracing-spring-boot-autoconfigure và 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ạiQuy ước đặt tênVí dụ thực tế
Starter chính thức từ Springspring-boot-starter-{name}spring-boot-starter-web
Starter bên thứ ba hoặc nội bộ{name}-spring-boot-starteracme-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:

XML
<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:

YAML
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: autoconfigure chứa mã cấu hình và starter chỉ là POM rỗng kéo phụ thuộc.
  • File META-INF/spring/...AutoConfiguration.imports là 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

Tự kiểm tra
0/5 câu đã trả lời
  1. Q1
    Tạ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)?
  2. Q2
    Tệ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?
  3. Q3
    Theo 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?
  4. Q4
    Tạ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?
  5. Q5
    Endpoint 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.

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

Bài tiếp theo

@EnableAutoConfiguration: Cơ Chế Đọc File Imports