Spring Boot/Module 01 — Tổng kết & cheat sheet
~15 phútSpring là gì & nền tảng IoCMiễn phí lượt xem

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

ConceptKhi nào dùngPitfall thường gặp
Constructor injectionDefault cho mọi bean business — final field, immutable, test ngoài SpringLombok @RequiredArgsConstructor + manual constructor → 2 constructor xung đột
Setter injectionChỉ cho dep optional, @Autowired(required=false)Field không final được, race condition nếu re-set
Field injectionKHÔNG dùng — anti-patternTest phải dùng reflection, không phát hiện class quá lớn
@ComponentClass business bạn viếtQuên @ComponentScan package → không scan được
@Bean methodObject third-party hoặc cần factory logicLite mode proxyBeanMethods=false → method gọi method khác ra 2 instance
@ConfigurationGom @Bean method + cấu hình containerfinal class → CGLIB không subclass được, startup fail
@Autowired (Spring)Default cho code SpringConstructor 1 param không cần @Autowired (Spring 4.3+)
@Inject (JSR-330)Khi viết library cross-DI-containerKhông có required=false, phải dùng Optional<T>
@ResourceKhi nhiều bean cùng type, lookup byNameKhông inject Collection được như @Autowired
@Qualifier("name")Disambiguate khi 2+ bean cùng typeTên mặc định = class lowercase chữ đầu, không phải full name
@Primary1 bean default trong nhiều bean cùng typeCó 2 @Primary cùng type → ambiguity vẫn fail
List<T> / Map<String,T> injectStrategy pattern, plugin architectureMap key = bean name, không phải custom code
@PostConstructInit logic dùng dep đã injectThrow exception → app fail to start (intended fail-fast)
@PreDestroyCleanup khi container shutdownKHÔNG chạy với prototype scope hoặc SIGKILL
singleton (default)95% caseInject prototype field → prototype semantics mất
prototypeStateful bean cần fresh instanceContainer không track destroy — caller phải cleanup
request / sessionHTTP-bound stateInject vào singleton → cần proxy proxyMode=TARGET_CLASS
@LazyDefer bean creation, phá circular depMethod call đầu chậm hơn — proxy overhead nhỏ
ObjectProvider<T>Defer cao nhất, hoặc bean optionalMỗi call resolve lại — không nên dùng nếu chỉ cần singleton
@TransactionalService method DB transactionSelf-call this.method() bypass proxy → tx không active
Graceful shutdownProduction K8sserver.shutdown=graceful + terminationGracePeriodSeconds ≥ Spring timeout + 30s

📖 Glossary module

Thuật ngữĐịnh nghĩa 1 câuNguồn
IoC (Inversion of Control)Design principle: framework gọi code bạn viết, thay vì code bạn gọi frameworkBài 02
DI (Dependency Injection)1 implementation của IoC: framework tạo dependency và đưa vào object qua constructor/setter/fieldBà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 EJBBài 01
BeanObject được Spring container quản lý lifecycleBài 02
BeanFactoryInterface gốc của container — DI engine tối giảnBài 03
ApplicationContextInterface đầy đủ — BeanFactory + i18n + event + environmentBài 03
Component scanQuá trình container quét classpath tìm class có @ComponentBài 06
Bean definitionMetadata mô tả 1 bean (class, scope, dep) — chưa instantiateBài 03
refresh()12-step method khởi tạo container, đỉnh cao của ApplicationContext lifecycleBài 03
BeanPostProcessor (BPP)Hook chạy trước/sau init mỗi bean — nơi AOP wrap proxyBài 04
JDK Dynamic ProxyProxy implement interface, runtime tạo qua Proxy.newProxyInstance()Bài 04
CGLIB ProxyProxy subclass extend bean class, runtime tạo bằng bytecode generationBài 04
AOP (Aspect-Oriented Programming)Programming paradigm tách cross-cutting concern (logging, tx, security) khỏi business logicBài 04
*Aware interface10+ interface cho phép bean nhận tham chiếu infrastructure (BeanFactoryAware, ApplicationContextAware...) — chỉ dùng cho infrastructure codeBài 04
Singleton scope1 instance/container — defaultBài 05
Prototype scope1 instance/getBean() — container không destroyBài 05
Scope-mismatch trapSingleton inject prototype field thường → prototype semantics mấtBài 05
@Configuration full modeproxyBeanMethods=true (default) — CGLIB enhance để @Bean method gọi nhau trả singletonBài 06
@Configuration lite modeproxyBeanMethods=false — không proxy, cần cho native imageBài 06
JSR-250Chuẩn Java cho @PostConstruct, @PreDestroy — Spring tuân thủ từ 2.5Bài 04
JSR-330Chuẩ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, @PostConstruct callback.
    • 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).
  • Choose giữa @Configuration full-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ề BeanFactory internals.

Paper / RFC / JEP:

Video / Talk:

Source code để đọc khi cần đi sâu:

Blog series:

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?