Module 01 — 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 01 đã bóc tách 6 concept lõi của Spring IoC: bài toán Spring giải, IoC vs DI, ApplicationContext nội tại, bean lifecycle 9 giai đoạn, 4 scope, 3 cách khai báo bean. Mini-challenge bài 07 chứng minh "Spring không phải magic". Đây là 1 trang để bookmark — quay lại khi gặp bug bean injection, transaction rollback, hoặc startup error. Cheat sheet 21 dòng + glossary 21 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ừ năm 2001-2003: Java EE/EJB 2.x quá phức tạp, Rod Johnson viết "Expert One-on-One J2EE" và open-source Spring 1.0 năm 2004 — "spring after a long winter of J2EE". Bạn hiểu rằng Spring không phải về annotation, mà về đảo ngược trách nhiệm lắp ráp object từ business code sang container. Bài 02 phân biệt rõ IoC (principle) và DI (1 implementation), 3 hình thức inject với constructor là default khuyến nghị. Bài 03 mở ApplicationContext ra: extend BeanFactory với i18n/event/environment, refresh() 12 bước cố định. Bài 04-05 đào sâu nội tại bean: 9 giai đoạn lifecycle, 4 scope, AOP proxy wrap ở BPP.postProcessAfterInitialization — đó là lý do @Transactional self-call không hoạt động. Bài 06 chốt 3 cách khai báo bean và sự khác biệt full mode CGLIB vs lite mode. Bài 07 mini-challenge — bạn build 1 IoC container 80 dòng, chứng minh container chỉ là reflection + topological sort + map cache.
🗺️ Cheat sheet
| Concept | Khi nào dùng | Pitfall thường gặp |
|---|---|---|
| Constructor injection | Default cho mọi bean business — final field, immutable, test ngoài Spring | Lombok @RequiredArgsConstructor + manual constructor → 2 constructor xung đột |
| Setter injection | Chỉ cho dep optional, @Autowired(required=false) | Field không final được, race condition nếu re-set |
| Field injection | KHÔNG dùng — anti-pattern | Test phải dùng reflection, không phát hiện class quá lớn |
@Component | Class business bạn viết | Quên @ComponentScan package → không scan được |
@Bean method | Object third-party hoặc cần factory logic | Lite mode proxyBeanMethods=false → method gọi method khác ra 2 instance |
@Configuration | Gom @Bean method + cấu hình container | final class → CGLIB không subclass được, startup fail |
@Autowired (Spring) | Default cho code Spring | Constructor 1 param không cần @Autowired (Spring 4.3+) |
@Inject (JSR-330) | Khi viết library cross-DI-container | Không có required=false, phải dùng Optional<T> |
@Resource | Khi nhiều bean cùng type, lookup byName | Không inject Collection được như @Autowired |
@Qualifier("name") | Disambiguate khi 2+ bean cùng type | Tên mặc định = class lowercase chữ đầu, không phải full name |
@Primary | 1 bean default trong nhiều bean cùng type | Có 2 @Primary cùng type → ambiguity vẫn fail |
List<T> / Map<String,T> inject | Strategy pattern, plugin architecture | Map key = bean name, không phải custom code |
@PostConstruct | Init logic dùng dep đã inject | Throw exception → app fail to start (intended fail-fast) |
@PreDestroy | Cleanup khi container shutdown | KHÔNG chạy với prototype scope hoặc SIGKILL |
singleton (default) | 95% case | Inject prototype field → prototype semantics mất |
prototype | Stateful bean cần fresh instance | Container không track destroy — caller phải cleanup |
request / session | HTTP-bound state | Inject vào singleton → cần proxy proxyMode=TARGET_CLASS |
@Lazy | Defer bean creation, phá circular dep | Method call đầu chậm hơn — proxy overhead nhỏ |
ObjectProvider<T> | Defer cao nhất, hoặc bean optional | Mỗi call resolve lại — không nên dùng nếu chỉ cần singleton |
@Transactional | Service method DB transaction | Self-call this.method() bypass proxy → tx không active |
| Graceful shutdown | Production K8s | server.shutdown=graceful + terminationGracePeriodSeconds ≥ Spring timeout + 30s |
📖 Glossary module
| Thuật ngữ | Định nghĩa 1 câu | Nguồn |
|---|---|---|
| IoC (Inversion of Control) | Design principle: framework gọi code bạn viết, thay vì code bạn gọi framework | Bài 02 |
| DI (Dependency Injection) | 1 implementation của IoC: framework tạo dependency và đưa vào object qua constructor/setter/field | Bài 02 |
| POJO (Plain Old Java Object) | Class Java thường, không extend/implement framework class — Rod Johnson coined để đối lập với EJB | Bài 01 |
| Bean | Object được Spring container quản lý lifecycle | Bài 02 |
| BeanFactory | Interface gốc của container — DI engine tối giản | Bài 03 |
| ApplicationContext | Interface đầy đủ — BeanFactory + i18n + event + environment | Bài 03 |
| Component scan | Quá trình container quét classpath tìm class có @Component | Bài 06 |
| Bean definition | Metadata mô tả 1 bean (class, scope, dep) — chưa instantiate | Bài 03 |
refresh() | 12-step method khởi tạo container, đỉnh cao của ApplicationContext lifecycle | Bài 03 |
| BeanPostProcessor (BPP) | Hook chạy trước/sau init mỗi bean — nơi AOP wrap proxy | Bài 04 |
| JDK Dynamic Proxy | Proxy implement interface, runtime tạo qua Proxy.newProxyInstance() | Bài 04 |
| CGLIB Proxy | Proxy subclass extend bean class, runtime tạo bằng bytecode generation | Bài 04 |
| AOP (Aspect-Oriented Programming) | Programming paradigm tách cross-cutting concern (logging, tx, security) khỏi business logic | Bài 04 |
*Aware interface | 10+ interface cho phép bean nhận tham chiếu infrastructure (BeanFactoryAware, ApplicationContextAware...) — chỉ dùng cho infrastructure code | Bài 04 |
| Singleton scope | 1 instance/container — default | Bài 05 |
| Prototype scope | 1 instance/getBean() — container không destroy | Bài 05 |
| Scope-mismatch trap | Singleton inject prototype field thường → prototype semantics mất | Bài 05 |
@Configuration full mode | proxyBeanMethods=true (default) — CGLIB enhance để @Bean method gọi nhau trả singleton | Bài 06 |
@Configuration lite mode | proxyBeanMethods=false — không proxy, cần cho native image | Bài 06 |
| JSR-250 | Chuẩn Java cho @PostConstruct, @PreDestroy — Spring tuân thủ từ 2.5 | Bài 04 |
| JSR-330 | Chuẩn Java cho @Inject, @Qualifier, @Named — Spring tuân thủ | Bài 02 |
⚠️ Pitfall tổng hợp
10 pitfall lớn nhất gom từ section "Nhầm" của các bài. Mỗi pitfall: code sai → code đúng + lý do.
1. @Autowired field trong constructor
// SAI:
@Service
public class S {
@Autowired private Foo foo;
public S() { foo.doStuff(); } // NPE -- @Autowired chua chay
}
// DUNG:
@Service
public class S {
private final Foo foo;
public S(Foo foo) { this.foo = foo; foo.doStuff(); }
}
Lý do: constructor chạy ở bước 1, populate field chạy ở bước 2. Constructor injection đưa dep vào parameter → có ngay khi constructor chạy.
2. Self-call @Transactional
// SAI:
@Service
public class OrderService {
public void place(Order o) { this.audit(o); } // bypass proxy
@Transactional
public void audit(Order o) { ... } // tx KHONG active
}
// DUNG: tach class
@Service
public class OrderService {
private final AuditService audit;
public OrderService(AuditService audit) { this.audit = audit; }
public void place(Order o) { audit.log(o); } // qua proxy -- tx active
}
Lý do: AOP proxy wrap object từ ngoài. Self-call (this.x()) là direct method call, không qua proxy → annotation bị bypass.
3. Circular dependency qua field injection
// SAI: app start "thanh cong" Boot 2.6 tro xuong, tham hoa runtime
@Service public class A { @Autowired private B b; }
@Service public class B { @Autowired private A a; }
// DUNG: refactor extract logic chung
@Service public class C { /* shared logic */ }
@Service public class A { A(C c) { ... } }
@Service public class B { B(C c) { ... } }
Lý do: circular dep là design smell. Spring "giải" bằng early reference dẫn đến state subtle bug. Boot 2.6+ tắt mặc định — fix gốc là refactor.
4. Service Locator anti-pattern
// SAI:
@Service
public class S {
@Autowired ApplicationContext ctx;
public void work() { ctx.getBean(Foo.class).doStuff(); }
}
// DUNG:
@Service
public class S {
private final Foo foo;
public S(Foo foo) { this.foo = foo; }
public void work() { foo.doStuff(); }
}
Lý do: hidden dependency, test khó (mock ApplicationContext khổng lồ), runtime failure thay vì startup failure.
5. Bean class final với @Transactional
// SAI:
@Service
@Transactional
public final class S { ... } // CGLIB khong subclass duoc -- startup fail
// DUNG:
@Service
@Transactional
public class S { ... } // bo final
Lý do: Boot 2.0+ ép CGLIB. CGLIB tạo proxy bằng cách subclass — final class không subclass được.
6. @PreDestroy trên prototype scope
// SAI:
@Component
@Scope("prototype")
public class S {
@PreDestroy public void cleanup() { ... } // KHONG chay
}
// DUNG: caller cleanup, hoac chuyen sang singleton
@Component
public class S { // singleton
@PreDestroy public void cleanup() { ... }
}
Lý do: Spring không track destroy của prototype — caller chịu trách nhiệm cleanup.
7. Nhầm @Resource với @Autowired cho Collection
// SAI:
@Resource
private List<PaymentGateway> gateways; // @Resource khong inject Collection
// DUNG:
@Autowired
private List<PaymentGateway> gateways;
Lý do: @Resource lookup 1 bean theo name. @Autowired mới gom Collection của tất cả implementation.
8. @Configuration(proxyBeanMethods=false) với method gọi nhau
// SAI:
@Configuration(proxyBeanMethods = false)
public class Cfg {
@Bean DataSource ds() { return new HikariDataSource(); }
@Bean Service svc() { return new Service(ds()); } // ds() goi truc tiep -- instance moi!
}
// DUNG: dependency qua method parameter
@Configuration(proxyBeanMethods = false)
public class Cfg {
@Bean DataSource ds() { return new HikariDataSource(); }
@Bean Service svc(DataSource ds) { return new Service(ds); } // Spring inject singleton
}
Lý do: lite mode không CGLIB enhance — ds() direct call tạo instance mới, không lookup container.
9. SIGKILL container production — @PreDestroy không chạy
# SAI: K8s default 30s grace, app cleanup mat 35s -- SIGKILL
spec:
terminationGracePeriodSeconds: 30 # default
# DUNG:
spec:
terminationGracePeriodSeconds: 60 # lon hon spring.lifecycle.timeout
# Plus Spring config:
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
Lý do: K8s grace period phải lớn hơn Spring timeout để callback có thời gian chạy.
10. Inject *Aware thay vì constructor inject
// SAI:
@Service
public class S implements ApplicationContextAware {
private ApplicationContext ctx;
public void setApplicationContext(ApplicationContext c) { ctx = c; }
}
// DUNG:
@Service
public class S {
private final ApplicationContext ctx;
public S(ApplicationContext ctx) { this.ctx = ctx; }
}
Lý do: *Aware là tight coupling Spring API. Constructor inject hoạt động cho cả 7+ "infrastructure type" (Environment, ApplicationEventPublisher, ...).
✅ Self-assessment outcomes
Tick được hết các ô sau, bạn sẵn sàng Module 02. Nếu chưa: re-read bài tương ứng trước khi tiếp tục.
- Explain vì sao Spring tồn tại bằng cách so sánh với Java EE/EJB 2.x trên 3 chiều: vendor lock-in, testability, boilerplate.
- Nếu chưa: re-read bài 01 section 1-2 + section 8 ("Bối cảnh Java EE 2001" và "Vì sao Spring vẫn dominate năm 2026").
- Compare 3 hình thức DI (constructor/setter/field) và justify constructor injection với 4 lý do cụ thể.
- Nếu chưa: re-read bài 02 section 3 ("Dependency Injection — 3 hình thức") + section 3.4 (bảng so sánh).
- Diagnose circular dependency và áp dụng đúng 1 trong 3 fix: refactor /
@Lazy/ObjectProvider.- Nếu chưa: re-read bài 02 section 9 ("Circular dependency — 3 dạng và cách giải") + làm SelfCheck Q6 không xem giải thích.
- Implement IoC container 80 dòng từ scratch: scan
@Component, topological sort, constructor injection,@PostConstructcallback.- Nếu chưa: làm lại bài 07 mini-challenge không xem lời giải. Đây là acceptance test cho module.
- Predict đúng output của lifecycle order (instantiate → populate →
@PostConstruct→ BPP → ready).- Nếu chưa: re-read bài 04 section 1 (sơ đồ 9 giai đoạn) + làm SelfCheck Q1 (predict output
null/instance).
- Nếu chưa: re-read bài 04 section 1 (sơ đồ 9 giai đoạn) + làm SelfCheck Q1 (predict output
- Choose giữa
@Configurationfull-mode CGLIB và lite-mode dựa trên tradeoff cụ thể.- Nếu chưa: re-read bài 06 section về full vs lite mode + tradeoff GraalVM native.
🚀 What's next — Module 02: Spring Boot Foundations
Module 01 đào sâu Spring Framework lõi — IoC container và bean. Module 02 ghép phần còn lại của Spring Boot vào bức tranh: starter dependency, auto-configuration deep-dive, externalized config, profiles, logging. Bạn sẽ trace 1 request từ main() đến controller, xem mọi bean được Boot tự đăng ký, và hiểu chính xác @EnableAutoConfiguration đọc gì từ classpath.
Specifically, sau Module 02 bạn sẽ giải đáp: vì sao chỉ thêm spring-boot-starter-data-jpa vào pom.xml thì auto có EntityManager bean? Auto-config dùng @ConditionalOnClass ra sao? Profile activation order thế nào (env > arg > yml)?
→ Đi tới Module 02: Spring Boot Foundations
(Nếu Module 02 chưa có overview lesson, vào bài 01 — Spring Boot giải quyết gì tạm thời.)
📚 Tài liệu mở rộng
Sau Module 01, đây là roadmap đào sâu cho học viên muốn vượt khỏi syllabus:
Sách:
- Spring in Action, 6th Edition — Craig Walls (Manning, 2022). Chương 1-3 cover IoC + DI + container, paraphrase từ docs nhưng dễ tiếp cận.
- Pro Spring 6 with Kotlin — Iuliana Cosmina et al. (Apress, 2023). Chương 2-3 sâu hơn về
BeanFactoryinternals.
Paper / RFC / JEP:
- JSR-330 — Dependency Injection for Java — chuẩn
@Inject. Đọc spec 12 trang đủ hiểu. - JSR-250 — Common Annotations —
@PostConstruct,@PreDestroy. - Martin Fowler — Inversion of Control Containers and the Dependency Injection pattern (2004) — bài kinh điển hệ thống hoá thuật ngữ.
Video / Talk:
- Spring Tips: Better Beans (Josh Long, 2024) — Spring developer advocate giải thích bean management modern.
- Going Reactive with Spring 6 — Juergen Hoeller — Spring core lead trình bày kiến trúc.
Source code để đọc khi cần đi sâu:
AbstractAutowireCapableBeanFactory.doCreateBean()— method ~80 dòng thực hiện toàn bộ 9 giai đoạn lifecycle.AbstractApplicationContext.refresh()—refresh()12 bước trong 1 method.
Blog series:
- Baeldung Spring Tutorials — 100+ bài cover từng annotation/pattern nhỏ. Lookup khi gặp vấn đề cụ thể.
- Reflectoring — Tom Hombergs — sâu, opinionated, giải thích "why" tốt hơn Baeldung.
Chúc mừng — bạn đã hoàn thành Module 01. Bạn có nền tảng IoC vững chắc. Mọi annotation Spring sau này (@RestController, @Repository, @Transactional) đều xây trên container bạn vừa hiểu. Nghỉ 1 ngày cho concept lắng xuống, rồi vào Module 02.
Bài này có giúp bạn hiểu bản chất không?