Module 02 — Tổng kết & cheat sheet
Recap, cheat sheet 1 trang, glossary, pitfall tổng hợp, self-assessment outcomes. 1 trang để bookmark.
TL;DR: Module 02 đã bóc tách 5 trụ cột Spring Boot: bài toán Boot giải năm 2014, starter/BOM anatomy, auto-configuration chuỗi sự kiện từ classpath đến bean, 17 PropertySource ưu tiên, profile toggle bean theo môi trường, và logging stack JSON production-grade. Mini-challenge bài 07 chứng minh "5 trụ cột cộng lại = app chạy từ zero trong 60 giây". Đây là 1 trang để bookmark — quay lại khi gặp bug autoconfig, config không có effect, hay log không xuất hiện. Cheat sheet 18 dòng + glossary 18 thuật ngữ + 10 pitfall code sai/đúng + self-assessment 6 outcomes match _meta.json.
Đã đi qua những gì
Hành trình bắt đầu từ Spring 4 era: mỗi project mới tốn 60-90 phút boilerplate — khai báo 15+ dependency với version thủ công, viết web.xml, applicationContext.xml 30-100 dòng, cài Tomcat external, config log4j.xml. Phil Webb và Dave Syer ở Pivotal đề xuất giải pháp năm 2014: gom 5 thao tác đó thành defaults thông minh với cơ chế override formal. Kết quả là Spring Boot 1.0 — 18 dòng code thay thế 60 phút boilerplate.
Bài 02 bóc starter đến tận vật lý: mở jar spring-boot-starter-web ra thấy không có class — chỉ có pom.xml. Giá trị thực của starter không phải code mà là BOM spring-boot-dependencies quản version 200+ thư viện đã test tương thích. Upgrade Boot = upgrade toàn ecosystem nhất quán trong 1 dòng.
Bài 03 là insight lớn nhất module: AutoConfigurationImportSelector đọc file AutoConfiguration.imports từ mọi jar trên classpath, lấy 143 candidate, filter qua @ConditionalOn* (ASM bytecode reader, không load class), chỉ ~30-50 autoconfig register. @ConditionalOnMissingBean là cơ chế "user override default" — đăng ký bean của bạn, Boot tự rút lui.
Bài 04-05 bóc configuration và profile: 17 PropertySource theo ưu tiên (command line thắng env var thắng file), relax binding tự map SPRING_DATASOURCE_URL → spring.datasource.url, @ConfigurationProperties record type-safe + @Validated fail-fast. Profile không chỉ override file mà còn toggle bean qua @Profile — kết hợp với profile groups Boot 2.4+ là pattern DRY cho multi-environment deployment.
Bài 06 chốt logging stack: SLF4J facade + Logback impl + bridge libs. Boot 3.4 GA thêm structured logging ECS 1 dòng property. MDC correlation ID trace request xuyên layer. Dynamic log level runtime qua Actuator — không cần restart app để bật DEBUG production.
Bài 07 mini-challenge — bạn build app trace đầy đủ startup → request → response bằng BeanPostProcessor + LoggingFilter MDC. Đây là template production-grade bạn dùng lại trong mọi module tiếp theo.
🗺️ Cheat sheet
| Concept | Khi nào dùng | Pitfall thường gặp |
|---|---|---|
spring-boot-starter-parent | 90% project — 1 dòng <parent> setup full BOM + plugin defaults | Project đã có corporate parent → không dùng được; dùng import scope thay |
BOM spring-boot-dependencies | Project có corporate parent, cần Boot version quản | Phải config Java version và plugin defaults thủ công |
Khai báo dep không <version> | Mọi dep Spring/Boot trong Boot project | Version explicit override BOM → mismatch toàn ecosystem |
@SpringBootApplication | Class main entry point | Đặt sai package → @ComponentScan không quét đủ bean |
SpringApplication.run() | Entry point app — 14 bước, bước 12 là refresh() | Khởi tạo bean quá nặng trong @PostConstruct → startup chậm; dùng ApplicationReadyEvent thay |
AutoConfiguration.imports | File liệt kê autoconfig trong jar starter custom | Quên file này → autoconfig class không bao giờ load |
@ConditionalOnClass | Autoconfig chỉ register khi class có classpath | Dùng value=SomeClass.class trong autoconfig → compile fail nếu class không có; dùng name="..." string |
@ConditionalOnMissingBean | Default bean — rút lui khi user override | Thiếu annotation → ambiguous bean conflict khi user register cùng type |
/actuator/conditions | Debug "tại sao bean không tạo" | Không expose endpoint → không debug được; bật ít nhất trong dev |
@ConfigurationProperties record | Group config cùng prefix, type-safe | Quên @ConfigurationPropertiesScan → record không bind, inject fail |
@Validated + @NotBlank | Fail-fast khi thiếu config bắt buộc | Không validate → field null → NPE runtime sau startup |
| Relax binding | K8s SPRING_DATASOURCE_URL → spring.datasource.url | Env var có dấu . không work trong bash shell; dùng UPPER_SNAKE_CASE |
application-prod.yml | Override config cho profile prod | Hardcode secret → security incident; dùng ${DB_PASSWORD} reference env var |
| Profile groups | Gom logic prod = prod-db + prod-cache | Typo profile name → silent fallback default, bug ngầm |
@Profile("prod & monitoring") | Bean chỉ register khi cả 2 profile active | & operator cần Spring 6 / Boot 3+; dùng array {"prod","monitoring"} cho OR |
log.error("failed", e) | Log exception với stack trace đầy đủ | log.error("failed: " + e.getMessage()) mất stack trace |
| MDC correlation ID | Trace request xuyên layer bằng requestId | Quên MDC.clear() trong finally → requestId leak sang request khác |
/actuator/loggers POST | Dynamic log level runtime không restart | Không bảo vệ endpoint → attacker bật DEBUG, log volume DDoS |
📖 Glossary module
| Thuật ngữ | Định nghĩa 1 câu | Nguồn |
|---|---|---|
| Spring Boot | 5 lớp đóng gói trên Spring Framework: starter, auto-config, embedded server, production features, build plugin | Bài 01 |
| Starter | Jar rỗng class chỉ chứa pom.xml liệt kê transitive deps — cú pháp ngắn khai báo nhóm dep | Bài 02 |
| BOM (Bill of Materials) | File pom chỉ có dependencyManagement định nghĩa version cho 200+ lib — không pull lib | Bài 02 |
spring-boot-dependencies | BOM chính của Boot — Pivotal test mọi tổ hợp version tương thích trước release | Bài 02 |
spring-boot-starter-parent | Parent pom inherit BOM + thêm Java version default, UTF-8, plugin defaults | Bài 02 |
@EnableAutoConfiguration | Annotation kích hoạt AutoConfigurationImportSelector load toàn bộ autoconfig | Bài 03 |
AutoConfigurationImportSelector | Class implement DeferredImportSelector — đọc AutoConfiguration.imports, filter, register | Bài 03 |
AutoConfiguration.imports | File text liệt kê fully-qualified class name của mọi autoconfig trong 1 jar | Bài 03 |
@ConditionalOnMissingBean | Condition: register bean chỉ khi user chưa register cùng type — cơ chế "rút lui" của Boot | Bài 03 |
@ConditionalOnClass | Condition: register bean chỉ khi class tên này có trong classpath | Bài 03 |
| PropertySource | Nguồn cung cấp property cho Spring Environment — Boot có 17 nguồn theo thứ tự ưu tiên | Bài 04 |
| Relax binding | Cơ chế Boot tự convert giữa kebab-case / camelCase / UPPER_SNAKE_CASE khi bind property | Bài 04 |
@ConfigurationProperties | Annotation bind group property cùng prefix vào POJO/record — type-safe, validate được | Bài 04 |
| Profile | Named tag gắn vào bean hoặc config — Boot toggle registration theo profile active | Bài 05 |
| Profile groups | Boot 2.4+ — gom nhiều profile thành 1 logic tên: activate 1 tên, Boot include cả nhóm | Bài 05 |
| SLF4J | Simple Logging Facade for Java — facade interface, code app chỉ dùng API này, không bind implementation | Bài 06 |
| MDC (Mapped Diagnostic Context) | ThreadLocal map cho log — set correlation ID per-request, extract trong log pattern %X{key} | Bài 06 |
| ECS (Elastic Common Schema) | Schema JSON chuẩn cho Elasticsearch — Boot 3.4 GA support native qua logging.structured.format.console: ecs | Bài 06 |
⚠️ Pitfall tổng hợp
10 pitfall lớn nhất gom từ section "Nhầm" và "Pitfall" của các bài. Mỗi pitfall: code sai → code đúng + lý do.
1. Khai báo version explicit cho dep Spring trong Boot project
<!-- SAI: override BOM, gay mismatch toan ecosystem -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0</version>
</dependency>
<!-- DUNG: de BOM resolve -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
Lý do: BOM spring-boot-dependencies đã set version tương thích. Override 1 lib → version lệch với các lib còn lại trong ecosystem → runtime bug tinh tế hoặc compile fail.
2. Thiếu @ConditionalOnMissingBean trong autoconfig
// SAI: user register cung type -> conflict
@AutoConfiguration
public class MyConfig {
@Bean
public CacheManager cacheManager() { return new ConcurrentMapCacheManager(); }
}
// DUNG: rut lui khi user override
@AutoConfiguration
public class MyConfig {
@Bean
@ConditionalOnMissingBean
public CacheManager cacheManager() { return new ConcurrentMapCacheManager(); }
}
Lý do: mọi @Bean trong autoconfig là "default" — phải rút lui khi user đăng ký bean cùng type. Thiếu → NoUniqueBeanDefinitionException khi user customize.
3. @ConditionalOnClass(value = ThirdPartyClass.class) trong autoconfig
// SAI: compile fail khi ThirdPartyClass khong co classpath
@AutoConfiguration
@ConditionalOnClass(KafkaTemplate.class)
public class MyKafkaConfig { ... }
// DUNG: string, khong require import
@AutoConfiguration
@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate")
public class MyKafkaConfig { ... }
Lý do: autoconfig phải compile được kể cả khi class điều kiện không có classpath. Dùng name string tránh compile dependency.
4. Hardcode secret trong application.yml
# SAI: commit git -> leak secret
spring:
datasource:
password: prod-secret-2026
# DUNG: reference env var
spring:
datasource:
password: ${DB_PASSWORD}
Lý do: file application.yml commit git → secret lộ. Repository scanner (GitGuardian, GitHub Secret Scanning) alert. Secret luôn qua env var / K8s Secret / Vault.
5. Thiếu @Validated trên @ConfigurationProperties
// SAI: field null -> NPE runtime sau khi app da start
@ConfigurationProperties(prefix = "app")
public record AppProps(String dbUrl) {}
// DUNG: fail-fast tai startup
@ConfigurationProperties(prefix = "app")
@Validated
public record AppProps(@NotBlank String dbUrl) {}
Lý do: thiếu validation → user quên set property → field null → NPE xảy ra runtime khi có request thay vì tại startup. Fail-fast luôn tốt hơn fail-late.
6. Quên @ConfigurationPropertiesScan hoặc @EnableConfigurationProperties
// SAI: record khong tu register
@ConfigurationProperties(prefix = "app")
public record AppProps(String name) {}
// DUNG: scan tu dong
@SpringBootApplication
@ConfigurationPropertiesScan
public class App { ... }
Lý do: @ConfigurationProperties không tự register thành bean. Cần scan hoặc explicit enable. Quên → inject AppProps fail với NoSuchBeanDefinitionException.
7. Profile name typo — app vẫn start
# SAI: typo 'prdo' -- app start voi default profile, bug ngam
java -jar app.jar --spring.profiles.active=prdo
# DUNG: validate startup
# @EventListener(ApplicationReadyEvent.class) check env.getActiveProfiles()
Lý do: Boot không validate tên profile — typo → 0 profile active → fallback default config → bug ngầm không có error message rõ ràng. Cần validation startup explicit.
8. Bean @Profile("prod") inject vào service không có @Profile
// SAI: NoSuchBeanDefinitionException khi dev profile active
@Component @Profile("prod")
public class CloudWatchExporter implements MetricsExporter { ... }
@Service
public class CommonService {
@Autowired MetricsExporter exporter; // null khi profile=dev
}
// DUNG: interface + 2 implementation
@Component @Profile("prod")
public class CloudWatchExporter implements MetricsExporter { ... }
@Component @Profile("!prod")
public class ConsoleExporter implements MetricsExporter { ... }
@Service
public class CommonService {
@Autowired MetricsExporter exporter; // moi profile co implementation
}
Lý do: service không có @Profile register ở mọi profile, nhưng dep của nó thiếu ở profile dev. Luôn cung cấp implementation cho mọi profile qua interface.
9. Log exception sai cách — mất stack trace
// SAI: chi log message, mat stack trace
log.error("Payment failed: " + e.getMessage());
log.error("Payment failed: {}", e);
// DUNG: exception la param cuoi
log.error("Payment failed for order {}", orderId, e);
Lý do: SLF4J detect param cuối là Throwable → log message + full stack trace. Thiếu stack trace = debug production mù.
10. Quên MDC.clear() trong filter — correlation ID leak
// SAI: exception trong chain -> MDC khong clear -> leak
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
MDC.put("requestId", id);
chain.doFilter(req, res);
MDC.clear(); // KHONG chay khi chain throw exception
}
// DUNG: try-finally dam bao clear
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
MDC.put("requestId", id);
try {
chain.doFilter(req, res);
} finally {
MDC.clear(); // luon chay
}
}
Lý do: Tomcat reuse thread — không clear MDC → thread T1 xử lý request A lỗi rồi xử lý request B với requestId của A → log corrupt correlation ID.
✅ Self-assessment outcomes
Tick được hết các ô sau, bạn sẵn sàng Module 03. Nếu chưa: re-read bài tương ứng trước khi tiếp tục.
- Explain 5 trụ cột Spring Boot và vì sao Boot ra đời năm 2014 để xoá boilerplate Spring 4.
- Nếu chưa: re-read bài 01 section 1-3 ("Bài toán Spring 4", "5 trụ cột", "Opinionated vs lock-in") + SelfCheck Q1.
- Trace chuỗi sự kiện từ classpath đến bean khi thêm 1 starter — bóc
AutoConfigurationImportSelector, fileAutoConfiguration.imports, và 12@ConditionalOn*family.- Nếu chưa: re-read bài 03 section 2-4 ("entry point", "file AutoConfiguration.imports", "family @ConditionalOn*") + SelfCheck Q1.
- Diagnose tại sao 1 autoconfig bean không được tạo bằng
/actuator/conditionstrong dưới 5 phút.- Nếu chưa: re-read bài 03 section 6 ("Debug autoconfig — Actuator /conditions") + SelfCheck Q3. Bật endpoint, curl
/actuator/conditions, search bean bị miss.
- Nếu chưa: re-read bài 03 section 6 ("Debug autoconfig — Actuator /conditions") + SelfCheck Q3. Bật endpoint, curl
- Implement type-safe configuration binding với
@ConfigurationPropertiesrecord +@Validated+ relax binding cho K8s env var.- Nếu chưa: re-read bài 04 section 5.2-5.3 (
@ConfigurationProperties+ records) + section 6 (relax binding) + SelfCheck Q6. Tự viết 1 record config và test bind từ env var.
- Nếu chưa: re-read bài 04 section 5.2-5.3 (
- Choose đúng profile layout (single file vs group vs multi-document YAML) cho multi-environment deployment với DRY.
- Nếu chưa: re-read bài 05 section 4-7 (groups, multi-document, inheritance) + SelfCheck Q4. Thiết kế layout 3-region từ scratch không nhìn tài liệu.
- Design logging stack cho production: structured JSON (Boot 3.4 ECS) + MDC correlation ID + dynamic log level qua Actuator mà không restart app.
- Nếu chưa: re-read bài 06 section 5-6 (structured logging + Actuator /loggers) + làm lại mini-challenge bài 07 phần "Mức 3-4". Bật
logging.structured.format.console: ecsvà verify JSON output.
- Nếu chưa: re-read bài 06 section 5-6 (structured logging + Actuator /loggers) + làm lại mini-challenge bài 07 phần "Mức 3-4". Bật
🚀 What's next — Module 03: Spring MVC & Web Layer
Module 02 đã bóc tách nền tảng Boot — 5 trụ cột, auto-config, config, profile, logging. Module 03 bước vào Web Layer: Spring MVC architecture, DispatcherServlet routing, @RestController đầy đủ, validation với Jakarta Validation, exception handling chuẩn, và OpenAPI/Swagger documentation. Bạn sẽ build REST API CRUD đầu tiên cho domain TaskFlow — capstone xuyên suốt khoá.
Specifically, sau Module 03 bạn sẽ giải đáp: DispatcherServlet route request qua bao nhiêu layer trước khi đến @GetMapping method? Vì sao @Valid trên @RequestBody không hoạt động khi thiếu 1 annotation? @RestControllerAdvice khác @ControllerAdvice ở chỗ nào và khi nào dùng cái nào?
→ Đi tới Module 03: Spring MVC & Web Layer
📚 Tài liệu mở rộng
Sách:
- Spring Boot in Practice — Somnath Musib (Manning, 2022). Chương 1-3 cover Boot foundations với nhiều ví dụ thực tế.
- Learning Spring Boot 3 — Greg L. Turnquist (Packt, 2023). Mỗi chapter 1 project nhỏ — học qua làm.
Paper / RFC / JEP:
- Spring Boot Reference — Auto-configuration — tài liệu chính chủ auto-config.
- Spring Boot Reference — Externalized Configuration — 17 PropertySource đầy đủ.
- Spring Boot Reference — Structured Logging — Boot 3.4 GA announcement.
- Elastic Common Schema (ECS) — schema JSON chuẩn cho log field.
Video / Talk:
- Josh Long — Spring Tips: Auto-configuration — Spring developer advocate giải thích cơ chế autoconfig live.
- Stéphane Nicoll — Behind Spring Boot Auto-configuration — Boot maintainer phân tích internals.
Source code để đọc:
AutoConfigurationImportSelector— class load autoconfig, methodselectImports.DataSourceAutoConfiguration— đại diện pattern autoconfig. Đọc 1 lần, hiểu tất cả.AutoConfiguration.imports— 143 autoconfig class. Scroll qua 1 lần để thấy scope.
Blog series:
- Baeldung Spring Boot — 200+ bài cover từng annotation/pattern. Lookup khi gặp vấn đề cụ thể.
- Spring Blog — release notes, feature announcement từ Pivotal team. Theo dõi Boot 3.x changelog.
Chúc mừng — bạn đã hoàn thành Module 02. Bạn hiểu Spring Boot không phải magic — là Java code với pattern @ConditionalOn* lặp đi lặp lại có thể đọc và trace được. Mọi auto-configuration đều có lý do, mọi property đều có source, mọi log đều có correlation ID. Nghỉ 1 ngày cho concept lắng xuống, rồi vào Module 03 — Web Layer.
Bài này có giúp bạn hiểu bản chất không?